DPoP (Demonstrating Proof-of-Possession, RFC 9449) cryptographically binds an access token to a public/private keypair your client holds. A stolen DPoP-bound token is useless to an attacker without the matching private key — every API call requires a freshly-signed proof JWT. This page is for integrations where bearer tokens aren’t enough.Documentation Index
Fetch the complete documentation index at: https://docs.flexslot.gg/llms.txt
Use this file to discover all available pages before exploring further.
Should you use DPoP?
| Situation | Use DPoP? |
|---|---|
| Server-side app handling its own tokens | Optional. Bearer is fine if your server is secure. |
| Public client (SPA, mobile) handling high-value scopes | Yes, strongly recommended. |
| Server-side app where token leakage is a known concern (shared logging, third-party error trackers) | Yes. |
Any client with decks:write, sideboards:write, guides:write and a security mandate | Yes. |
| Internal scripts using a PAT | No, doesn’t apply |
| You’re not sure | Start with Bearer. Add DPoP when you ship to production scale. |
DPoP is opt-in at Flexslot. Set
dpop_bound_access_tokens: true on your OAuth client in the partner admin to require DPoP for all tokens issued to that client.How DPoP works
The key insight: every single API request gets its own short-lived proof JWT, signed by the client’s private key, that names the URL, method, and the hash of the access token. An attacker who steals the access token can’t make the proof.Set up your keypair
DPoP supportsES256 (ECDSA P-256), ES384, EdDSA, and RS256. Use ES256 unless you have a reason not to.
/token and all subsequent API calls.
Building a DPoP proof JWT
A DPoP proof is a JWT with: Header:Helper
Token exchange with DPoP
Add aDPoP header to the /token request:
"token_type": "DPoP" and a cnf claim with the thumbprint of your key:
cnf.jkt is the JWK Thumbprint of your public key. The resource server checks that every incoming request’s DPoP proof was signed by the key that hashes to this thumbprint.
Calling the resource server with DPoP
Two changes to every API call:- The
Authorizationscheme isDPoP, notBearer. - Add a
DPoPheader with a fresh proof that includesath.
Refreshing DPoP-bound tokens
Refresh tokens for a DPoP client are also bound to the key. The/token request to refresh must include a DPoP proof signed by the same key as the original token exchange.
DPoP errors
| Error | HTTP | Meaning |
|---|---|---|
invalid_dpop_proof | 400 / 401 | Proof JWT is malformed, signature doesn’t verify, or required claims missing |
invalid_token with WWW-Authenticate: DPoP | 401 | Token is bound to a different key than the one signing the proof |
use_dpop_nonce | 401 | Server is enforcing a nonce; retry with nonce claim (see below) |
Nonce mode
Flexslot may issue aDPoP-Nonce: <value> header at any time. When it does, your next DPoP proof must include the nonce as a top-level claim:
use_dpop_nonce, read the DPoP-Nonce response header and retry.
Trade-offs
| Pro | Con |
|---|---|
| Stolen token alone is worthless | Every request needs cryptographic signing — measurable CPU cost |
| Refresh token replay defense is built-in | Key management becomes part of your ops |
| Provable client identity at the RS | More complex to debug — DPoP proofs are JWTs you’ll squint at |
| Per-request audit (jti) | Requires backend support — many SDK paths don’t have DPoP yet |
When NOT to use DPoP
- For very low-value reads where the convenience of Bearer outweighs the marginal risk.
- For machine-to-machine flows where mTLS (RFC 8705) is a better fit (continuous mutual auth, not per-request).
- For environments where your TLS terminator strips the
DPoPheader — fix the infra first.
Reference
- RFC 9449 — The DPoP spec.
- RFC 7638 — JWK Thumbprint (how
cnf.jktis computed). - jose for Node — Recommended JOSE library.
- jwcrypto for Python — JWS/JWK support including DPoP.