Articles

What Is DPoP? Sender-Constrained OAuth Tokens Explained

DPoP binds OAuth access tokens to a client-held key, so a stolen token is useless without the private key. How proofs work and when to use DPoP.

Chisato Chisato · · 5 min read
A set of keys on a ring against a plain background

DPoP, short for Demonstrating Proof of Possession, is an extension to OAuth 2.0 that binds an access token to a cryptographic key held by the client. Every time the client uses the token, it must also send a fresh, signed proof that it holds the matching private key. A token stolen from logs, a proxy, or a compromised browser extension is useless on its own, because the attacker can’t produce valid proofs. DPoP turns bearer tokens into sender-constrained tokens.

The problem with bearer tokens

Most OAuth access tokens are bearer tokens: whoever presents the token gets access, no questions asked. That’s convenient, and it’s the model behind the Authorization: Bearer ... header you see in nearly every API. It’s also the core weakness. As our explainer on JWTs puts it, a bearer token is like cash — possession is the only credential.

Tokens leak in ordinary ways: verbose server logs, misconfigured analytics, browser storage reachable by injected scripts, intermediaries that terminate TLS. Short expiry limits the damage window, but within that window a leaked token can be replayed freely. That’s a classic replay attack, and it’s the gap DPoP is designed to close.

How DPoP works

DPoP adds one new artifact: the DPoP proof, a small JWT the client signs with its private key and sends in a DPoP HTTP header. The flow has three parts.

1. The client generates a key pair. Typically an asymmetric key such as an elliptic-curve P-256 key. In browsers, the Web Crypto API can create it as non-extractable, so JavaScript can use it for signing but can never read the raw private key.

2. The client requests a token with a proof. When calling the authorization server’s token endpoint, the client includes a DPoP proof. The server verifies the proof, then issues an access token bound to that public key. In a JWT access token, the binding appears as a confirmation claim, cnf, containing jkt — the SHA-256 thumbprint of the client’s public key. The token response reports token_type as DPoP instead of Bearer.

3. The client calls APIs with token plus proof. Each request carries two headers:

GET /api/orders HTTP/1.1
Host: api.example.com
Authorization: DPoP eyJhbGciOiJFUzI1NiIs...
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2Iiwiandr...

The resource server checks that the proof is valid and that the key which signed it matches the thumbprint bound into the access token. No match, no access.

What’s inside a DPoP proof

The proof’s header declares typ: "dpop+jwt", the signing algorithm, and the client’s public key as a jwk. The payload is deliberately narrow:

ClaimMeaning
jtiA unique ID for this proof, so the server can reject reuse
htmThe HTTP method of the request, such as POST
htuThe target URI, without query string or fragment
iatWhen the proof was created
athHash of the access token (included when calling a resource server)
nonceA server-supplied value, when the server requires one

Together these pin each proof to one request: one method, one URL, one moment, one token. You can paste a proof into our JWT decoder to see the structure — it’s an ordinary signed JWT, just with a very specific job.

What the server has to verify

A resource server accepting DPoP should check, on every request:

  1. The proof is a well-formed JWT with typ set to dpop+jwt and an asymmetric algorithm it allows.
  2. The signature verifies against the jwk embedded in the proof header.
  3. htm and htu match the actual request.
  4. iat is recent, within a tight tolerance.
  5. jti hasn’t been seen before within that window — this requires a short-lived replay cache.
  6. ath matches the hash of the presented access token.
  7. The thumbprint of the proof’s key equals the cnf.jkt value bound to the token.

Servers can also issue a DPoP-Nonce response header and require clients to echo it in the next proof. That limits how far in advance a proof can be generated, which matters if an attacker briefly gains the ability to sign.

DPoP vs mTLS-bound tokens

DPoP isn’t the only way to sender-constrain tokens. The older approach binds tokens to a client TLS certificate, as described in our mTLS explainer.

DPoPmTLS-bound tokens
Binding layerApplication (HTTP headers)Transport (TLS handshake)
Works in browsers / SPAsYes, via Web CryptoPoorly; client certs are awkward in browsers
Survives TLS-terminating proxiesYesNeeds certificate forwarding
Per-request proofYes, a new signed JWT each callNo; the TLS session is the proof
Operational overheadReplay cache, nonce handlingCertificate issuance and rotation
Typical fitPublic clients, mobile apps, SPAsServer-to-server, regulated APIs

The short version: mTLS is strong and efficient where you control both endpoints and the network path. DPoP is the practical option for public clients, especially single-page apps and mobile apps that can’t hold a client secret and already rely on PKCE.

What DPoP does not protect against

DPoP raises the bar for token theft, but it’s not a cure-all.

  • Code running in your client can still sign. If an attacker achieves cross-site scripting, a non-extractable key stops them from exfiltrating it, but their script can still ask the browser to sign proofs while the page is open. Server nonces and short lifetimes shrink this window; they don’t eliminate it.
  • It doesn’t replace TLS. DPoP proofs aren’t encrypted. Everything should still travel over HTTPS.
  • It adds state on the server. The jti replay check needs a shared cache across instances, which slightly undercuts the “stateless” appeal of JWT access tokens.
  • Refresh tokens matter too. For public clients, the authorization server should bind refresh tokens to the same key, or a stolen refresh token becomes the new weak link.

DPoP is one layer of defense against session hijacking, alongside content security policies, short token lifetimes, and careful storage choices.

When to adopt it

DPoP is worth considering when your API holds sensitive data, your clients are public (browser or mobile), and you can’t practically deploy client certificates. Most mature identity providers and OAuth libraries support it, so in practice adoption usually means enabling it on the authorization server, generating a key in the client, and adding proof validation middleware on resource servers. As with any auth plumbing, prefer a well-tested library over hand-rolling proof validation.

The takeaway

DPoP converts OAuth bearer tokens into sender-constrained tokens by binding them to a client-held key and requiring a fresh signed proof on every request. Each proof is tied to one method, URL, time, and token, so a leaked access token can’t be replayed elsewhere. It’s the browser-friendly alternative to mTLS-bound tokens — not a substitute for HTTPS or XSS defenses, but a meaningful upgrade over plain bearer tokens for public clients.

Chisato Chisato · · 5 min read

What Is an Open Redirect? Risks and How to Prevent It

An open redirect lets attackers use your domain to send users to any URL. How open redirect vulnerabilities work, why they matter, and how to fix them.

#Security #Web Development #Authentication
Chisato Chisato · · 4 min read

What Is a Man-in-the-Browser Attack?

A man-in-the-browser attack uses malware inside the browser itself to alter what a user sees and submits, bypassing HTTPS and session protections entirely.

#Security #Authentication #Web Development
The Lycoris Team The Lycoris Team · · 5 min read

CSRF vs. XSS: What's the Difference?

CSRF forges a request using a victim's login session; XSS runs the attacker's own code inside the victim's browser. Different mechanisms, different fixes.

#Security #Web Development #Authentication