Demonstrating Proof of Possession Overview
On this page
What is DPoP (Demonstration of Proof of Possession)?
Demonstration of Proof of Possession (DPoP) is a mechanism to sender-constrain access tokens. It allows you to bind an access token to the specific client that originally received it from the authorization server. As a result, the token is no longer a bearer token—meaning it can't be used by just anyone who obtains it. Instead, the client must present proof of possession to use the token.
Why Use DPoP?
DPoP is especially useful in cases where stronger methods of proof of possession, such as mutual TLS, are not possible or may prove too difficult to manage. DPoP is designed to provide a lightweight alternative in these environments, while still enhancing security by making sure access tokens can only be used by the rightful holder.
Standards and Specifications
DPoP is formally defined in RFC 9449, which outlines how this mechanism works in the context of OAuth 2.0 and related protocols. It's increasingly adopted in scenarios where securing access tokens without complex infrastructure is a priority.
How DPoP Works
To use DPoP, a client first uses a security library to provision a keypair. Some types of clients, like console apps, desktop apps, mobile apps and backend APIs can also use the operating system's support for secure key storage. In backend environments, each clustered instance of a workload can create their own distinct key.
DPoP relies on a simple principle: first, the client presents their public key to the authorization server, which is then incorporated into the access token. Next, the client sends the public key, encoded as a JWT, together with the access token to the resource server (RS). Thanks to this the RS can verify that the client is in possession of a private key and that it is the same key that was used when issuing the access token.

DPoP proofs are JWTs that must be signed by the client using a private key. You must use asymmetric signing for DPoP, so that server components can verify the client's key with only the corresponding public key.
Tying an Access Token to a Proof of Possession
When sending a request to the /token endpoint, the client adds a a signed DPoP proof JWT to the DPoP HTTP header, containing the following information:
- The public key that can be used to verify the signature.
- The
jticlaim, which is a unique identifier of the JWT. - The
htmandhtuclaims, which contain respectively the HTTP method and the URL to which the client sends the request. - The
iatclaim, which has the time when the JWT was issued.
The header and payload of a DPoP proof JWT sent to a /token endpoint can look something like this.
{"typ": "dpop+jwt","alg": "ES256","jwk": {"kty": "EC","x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs","y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA","crv": "P-256"}}{"jti": "-BwC3ESc6acc2lTc","htm": "POST","htu": "https://myidsvr.com/token","iat": 1598524639}
The authorization server should validate the JWT - the signature and claims contained in the token. If DPoP is deemed valid the AS adds a confirmation claim to the access token which is a Base64 urlencoding of the SHA-256 Thumbprint of the public key used to sign the DPoP proof JWT.
This claim can look similar to this in the resulting JWT access token:
"cnf": {"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"}
The jkt claim is used to indicate that the DPoP mechanism was used. Clients use the sender constrained access token multiple times to call APIs.
Proving Possession in Resource Servers
Once the client receives an access token with a confirmation claim it must provide a proof of possession whenever the token
is used to access resources. The client must send a new DPoP proof JWT for every request it makes to the RS. The JWT must be
constructed in the same way as for the /token endpoint. In particular, the htm and htu claims must have the values
conforming to each request being made.
The client sends the access token using the DPoP scheme and a DPoP header containing the DPoP proof JWT minted for the concrete request:
GET /resource HTTP/1.1Host: api.example.comAuthorization: DPoP eyJhbGci...hF1MQDPoP: eyJ0eXAi...4UCbQ
The resource server, should perform extra verification when receiving a DPoP proof JWT:
- Verify the signature of the DPoP proof JWT with the public key contained in the header of that token.
- Verify the claims of the DPoP proof JWT: whether the HTTP method and URL match the called endpoint and whether the token is not expired.
- Verify whether the public key used to sign the DPoP proof JWT matches the thumbprint from the
jktclaim of the access token.
Freshness of DPoP Proof JWTs
Both the authorization server and resource server must only allow DPoP proof JWTs for a short time, like a few minutes, to limit the potential impact of stolen proof JWTs. There are two mechanisms for doing so:
- The server compares the current time to a client timestamp, by verifying the
iatclaim of DPoP proof JWTs. - The server compares the current time to a server timestamp, that the server maps to a Server-Provided Nonce.
In practice, checking the client's iat claim is likely to have reliability issues in most environments, due to potential clock skew differences between clients and servers. Therefore, server-provided nonces are the preferred mechanism, where the server returns HTTP error responses of the following form. The authorization server's token endpoint use an HTTP 400 status code and resource servers use an HTTP 401 status code.
HTTP/1.1 400 Bad RequestDPoP-Nonce: 1787127767#jz82RaXNlENy-gHoa5FFPUtm3lhplODAOHUXCLK_ZLA{"error": "use_dpop_nonce","error_description": "use provided DPoP nonce"}
The client must process these responses as part of normal flows, and retry the server request with a new DPoP proof JWT that contains a nonce claim with the value from the DPoP-Nonce response HTTP header.
The server should also ensure that the client proves possession with a fresh DPoP proof on every server request. To do so, servers cache the jti claim so that they can detect DPoP proof replay. In practice, resilient DPoP therefore requires a server-side cache. Since server components are usually clustered, a DPoP server deployment requires a distributed cache.
DPoP Deployments
An authorization server that supports DPoP should have built-in support for server-provided nonces. Organizations that use DPoP need to implement additional resource server work to validate DPoP proof JWTs and return their own server-provided nonces.
You can treat DPoP for resource servers as an infrastructure concern, and implement proof of possession in an API gateway that interacts with a cache. The API gateway can verify the DPoP proof JWT and issue server-provided nonces. Doing so simplifies work for API developers, who only need to focus on JWT access token validation and business authorization. The following diagram shows an example deployment.
The example deployment demonstrates the following steps:
- During user authentication, the client sends a
dpop_jktparameter in its authorization request, with the thumbprint of its DPoP signing key. - The client sends a DPoP proof JWT in its token request, whose public key must match that sent earlier in the
dpop_jktparameter. - The client receives an opaque access token.
- The client calls an API with the opaque access token and also sends a fresh DPoP proof.
- The API gateway uses the Phantom Token Pattern to introspect the opaque access token and get a sender-constrained JWT access token.
- The API gateway runs a sender-constrained token plugin to implement DPoP proof of possession, with the help of a distributed cache.
- API developers receive a JWT access token, validate it and use its claims for business authorization.
When to Choose DPoP
The problem that DPoP solves is protection against stolen access tokens. It does not prevent a malicious party creating their own DPoP signing key and sending DPoP proof JWTs. To prevent that, combine DPoP with strong client authentication. The following table highlights a number of client types where you might consider the use of DPoP.
| Client Type | Can Use Secure Key Storage? | Can Protect Against Stolen Access Tokens? | Can Prevent Impersonation? |
|---|---|---|---|
| Console, desktop and mobile public clients | Yes | Yes | No |
| Attestation clients | Yes | Yes | Yes |
| Browser Clients | No | Yes | No |
| B2B API Clients | Yes | Yes | Yes |
| Microservices and AI Agents | Yes | Yes | Yes |
Public Clients
DPoP helps to harden public clients, since if they somehow leak an access token, e,g, by saving it in an insecure way, the access token alone does not enable someone who retrieved it to gain API access. However, a malicious party can potentially use the public client's client_id and their own DPoP signing key to gain access. Therefore, DPoP does not provide a complete security solution for public clients.
Browser Clients
Browser clients have their own security best practices. The critical connection is from the browser to backend entry points. The most secure way to manage that connection is to use a Backend for Frontend (BFF). Curity recommends the Token Handler Pattern to implement strongest browser security, instead of DPoP.
Attestation Clients
Some modern Android and iOS devices support attestation features that prevent client impersonation. An OAuth flow that uses attestation to authenticate a client elevates the client's security to the level of a confidential client. DPoP is highly recommended for this type of client, to complete a hardened end-to-end security lifecycle.
B2B APIs
DPoP is also a great choice for high security internet APIs, such as those used for B2B. Although financial-grade APIs can use Mutual TLS Sender Constrained Access Tokens, some organizations may find the X509 infrastructure difficult or impractical to manage. DPoP can combine strong security with manageability. A hardened B2B solution that uses DPoP could also use Client Assertions for client authentication.
Microservices and AI Agents
The same pattern could be used for internal API requests, such as those initiated by AI agents, MCP servers and microservices. DPoP proof validation could be implemented in middleware such as sidecars within a service mesh environment.
DPoP at scale can be problematic, though, since a cache could easily become a bottleneck. For internal APIs, a more scalable solution is to use a service mesh to provide each workload with an X509 key that it can use to get a sender-constrained access token. The Harden OAuth Client Credentials with SPIFFE X509 SVIDs explains the approach.
DPoP in the Curity Identity Server
The Curity Identity Server provides comprehensive support for DPoP. To start using DPoP, clients only need to start sending the DPoP parameters, in any of the following flows.
- Authorization Code
- Refresh Token
- Client Credentials
- Resource Owner Password Credentials
- Backchannel Authentication (CIBA)
- Token Exchange
- Pre-Authorized Code (Verifiable Credential Issuance)
You can use configuration settings to customize DPoP behaviors per OAuth client when required. The following screenshot shows the settings that you can change. Read about the options and the finer behaviors in the DPoP Docs.
The Curity Identity Server also uses DPoP and sender-constrained authentication access tokens (AATs) to secure authentication flows that use the Hypermedia Authentication API.
End-to-End Code Example
Curity provides an example deployment that you can run on a local computer, to understand both the client-side and server-side steps of a DPoP flow. The example treats resource server DPoP proof validation as an infrastructure concern and does the work in a Lua plugin that runs in an API gateway. As a result, nothing changes for API developers and API client developers only need to make simple code changes to use DPoP.
Conclusion
The DPoP mechanism provides a convenient way to harden client and API security with sender-constrained access tokens. It is particularly useful when you want to simplify infrastructure for remote clients. DPoP then upgrades bearer access tokens to sender-constrained access tokens, to protect against token theft.

Michal Trojanowski
Product Marketing Engineer at Curity
Customer Stories
Learn how organizations run identity and API security at scale.
Read customer storiesWas this helpful?