DPoP (RFC 9449) ties an OAuth access token to a key pair held by the client. Each request carries a signed proof, so a thief who steals only the token cannot use it. It does not help if malicious code is running inside the client itself.
The problem with bearer tokens
An ordinary bearer token works for whoever holds it. If it leaks through logs, a proxy, a compromised dependency or a stolen device backup, the thief can call the API as the user until it expires. The same goes for refresh tokens, which last longer.
How DPoP works
The client generates a key pair. With each request it sends a DPoP header holding a signed JWT, the proof.
The proof header has:
typofdpop+jwt- an asymmetric
alg, nevernoneor a MAC jwk, the public key, with no private material
The proof payload has:
jti: unique ID, to detect replayhtm: the HTTP methodhtu: the target URI without query or fragmentiat: creation timeath: base64url SHA-256 of the access token, when calling a resourcenonce: a server supplied value, when required
At the token endpoint, the authorization server binds the issued token to the proof key, recording the key's thumbprint in the cnf claim as jkt. The token type returned is DPoP. The resource server then checks the proof signature, method, URI, freshness and ath, and confirms the proof key matches jkt.
What it protects against
- Use of stolen access or refresh tokens by someone without the private key.
- Replay of a token at a different endpoint, since proofs are bound to method and URI.
- Pre generated proofs, especially when server nonces are used.
- Downgrade to bearer use of a DPoP bound token, which resource servers must reject.
Server nonces, delivered through the DPoP-Nonce header, let the server limit how long a proof stays valid.
What it does not protect against
- Malicious code in the client, such as cross site scripting, which can still create valid proofs while the client is online.
- Tampering with the body or most headers, since only the method and URI are signed. TLS still has to provide integrity.
- Replay within the validity window, unless servers track
jtivalues. - Authorization decisions. A valid proof is not permission.
RFC 9449 also says DPoP is not a substitute for secure transport.
Where it fits
RFC 9700 says refresh tokens for public clients must be sender constrained or rotated, and that sender constrained access tokens should be used, via mutual TLS or DPoP. DPoP is attractive for single page and mobile apps where mutual TLS is awkward.
Adoption checklist
- Check that your authorization server and APIs both support DPoP.
- Keep the private key non extractable where the platform allows.
- Track
jtivalues for a short window. - Use server nonces for high value APIs.
- Continue to use short access token lifetimes.
Quick check
Does DPoP make token theft impossible?
No. It stops use of a token by a party without the key. Code running inside the client can still sign proofs.
Is DPoP the same as mutual TLS?
Both bind tokens to a key. DPoP works at the application layer with a signed header, so it needs no client certificate handling.
Sources
- RFC 9449, OAuth 2.0 Demonstrating Proof of Possession
- RFC 9700, Best Current Practice for OAuth 2.0 Security
Last reviewed: 19 October 2026.