Skip to content
Ashish.
All posts
Diagram illustrating the DPoP flow and cnf claim binding.
6 min readSecurityBackend Developers, Security EngineersFeatured#dpop#cnf claim#security#oauth2#api security#bearer tokens#mtls

Sender-Constrained Tokens: DPoP and the cnf Claim

An examination of DPoP and the cnf claim for securing bearer tokens against theft and misuse in modern API architectures.

By Ashish Kumar

Bearer tokens in OAuth 2.0 are fundamentally "bearer" tokens, meaning possession alone grants access. If intercepted via XSS or log leakage, an attacker can misuse them regardless of TLS protections. DPoP (RFC 9449) solves this by cryptographically binding access tokens to clients using the cnf claim. This guide examines how DPoP shifts security from network trust to cryptographic proof, ensuring only the authorized client can use the token.

DPoP (Demonstrating Proof-of-Possession at the Application Layer), standardized in RFC 9449, addresses this by introducing a cryptographic binding between the access token and the client. This is achieved through the cnf (confirmation) claim embedded within the token itself. This guide examines the mechanism of DPoP, how the cnf claim operates, and why this shifts the security model from network trust to cryptographic proof.

The Vulnerability of Stateless Bearer Tokens

To understand DPoP, we must first define the failure mode of standard OAuth 2.0. In a standard flow, a Client (e.g., a Single Page Application or Mobile App) exchanges credentials for an access_token from the Authorization Server (AS). The Client then presents this token to a Resource Server (RS).

The RS validates the token's signature and expiry. If valid, the RS grants access. Crucially, the RS does not verify that the requestor is the original Client. It only verifies that the token is valid. This creates a "roaming token" problem. If a token is stolen, the attacker can replay it from any IP address, any user agent, or any location. The RS has no way to distinguish the legitimate client from the thief because the token contains no proof of identity other than its own signature.

DPoP solves this by requiring the client to demonstrate possession of a private key that corresponds to a public key presented during the token request. This creates a "sender-constrained" token.

The DPoP Flow: Cryptographic Binding

The DPoP mechanism operates in three distinct phases: Token Request, Token Response, and Resource Request. The core innovation is the introduction of a DPoP header in HTTP requests.

1. Key Generation and Proof Creation

The Client generates an asymmetric key pair (RSA or ECDSA). It does not send the private key to the AS. Instead, it creates a JWS (JSON Web Signature) header, known as the DPoP proof. This proof must include specific claims:

  • jti: A unique identifier for the proof, preventing replay attacks across different requests.
  • htm: The HTTP method of the request (GET, POST, etc.).
  • htu: The HTTP target URI.
  • iat: The Unix timestamp indicating when the proof was created, allowing the server to reject stale proofs.
  • jwk: The public key of the client.

By signing this payload with the client's private key, the client proves it holds the key, establishing a proof of possession. The AS receives this proof but never sees the private key.

{
  "typ": "dpop+jwt",
  "alg": "ES256",
  "jti": "a87ef5f6-95a1-4b39-9087-3d75375639c3",
  "htm": "POST",
  "htu": "https://api.example.com/resource",
  "iat": 1715631940,
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "f83OJ3D2xF1Bg8vub9tQu49H89R13GssVmUcLu9tXAs",
    "y": "ROy3iVkvNu2Qdxg5xk7Z1F_N3Rsbb3Q85AgjT9d9Q20"
  }
}

2. The Access Token and the cnf Claim

When the Client requests an access token, it includes the DPoP proof in the DPoP header of the token request. The AS validates the proof:

  1. It verifies the JWS signature using the jwk provided.
  2. It checks that the htm and htu in the proof match the token request endpoint.
  3. It ensures the jti has not been reused (replay protection).

Upon successful validation, the AS issues an access token. However, unlike a standard JWT, this token contains a cnf (confirmation) claim. The cnf value is a JSON object containing a jkt member — a base64url-encoded SHA-256 thumbprint of the client's public key.

{
  "sub": "user123",
  "aud": "https://api.example.com",
  "exp": 1715632000,
  "cnf": {
    "jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
  }
}

This step is critical. The token is now bound to the specific public key. If an attacker steals this token, they possess the token, but they do not possess the private key associated with the cnf claim.

3. Resource Server Validation

When the Client makes a request to the Resource Server, it must include both the Bearer token and a new DPoP proof. The proof's htm and htu must match the actual request being made.

The Resource Server performs the following validation chain:

  1. Validate the Bearer Token: Check signature, expiry, and audience.
  2. Extract cnf: Retrieve the jkt (JWK thumbprint) from the token's cnf claim.
  3. Validate DPoP Proof:
    • Verify the JWS signature using the public key from the proof's jwk header, confirming its thumbprint matches the jkt value in cnf.
    • Check that the jti in the proof is unique (preventing replay).
    • Verify that the htm and htu in the proof match the current HTTP request.

If the attacker uses the stolen token without the corresponding private key, they cannot generate a valid DPoP proof. The RS will reject the request with 401 Unauthorized and error code use_dpop_nonce or invalid_dpop_proof.

Why DPoP Over mTLS?

Transport Layer mTLS (Mutual TLS) is another method for sender constraint. In mTLS, the client presents a certificate during the TLS handshake. The RS verifies that the certificate used to establish the connection matches the one bound to the token (via the cnf claim in mTLS-bound tokens).

DPoP is preferred in many modern architectures for two reasons:

  1. Client Type Agnosticism: mTLS requires server-side certificate configuration, which adds operational overhead. DPoP works over standard TLS, allowing any client that can generate JWS signatures to use it without specialized infrastructure.
  2. Replay Protection: mTLS protects the channel but does not inherently bind the token to the specific HTTP method and URI of each request. DPoP's htm and htu claims provide granular, per-request binding, preventing an attacker from replaying a POST token as a GET request, or vice versa.

Furthermore, while mTLS provides robust channel security, DPoP offers a more flexible mtls binding alternative that does not require PKI management for every client. The cnf claim remains central to both models, but DPoP simplifies the client-side implementation for web and mobile applications.

Operational Considerations

Implementing DPoP introduces complexity. The AS must maintain state (or use a nonce mechanism) to prevent jti replay attacks. The nonce parameter in the token response can be used to force clients to refresh their DPoP proofs periodically, further limiting the window of opportunity for attackers.

Clients must securely store private keys. In browsers, use the Web Crypto API. In mobile apps, use secure enclaves or keystores. If a client's private key is compromised, the security guarantee is broken, but the impact is limited to that specific client instance, unlike a stolen bearer token which might grant broader access.

Conclusion

Bearer tokens are vulnerable by design. DPoP and the cnf claim restore the principle of least privilege by ensuring that only the entity possessing the specific private key can use the token. This shifts the security model from "trust the network" to "verify the proof." For backend developers and security engineers, adopting DPoP is a necessary step in hardening OAuth 2.0 implementations against token theft and replay attacks in modern API ecosystems.

Pitfalls

Common implementation errors can undermine DPoP security. First, failing to validate the htm and htu claims in the DPoP proof allows attackers to reuse a valid proof across different endpoints or methods. Second, ignoring the nonce parameter returned by the Authorization Server can lead to replay attacks if clients reuse the same proof. Finally, not handling nonce rotation correctly can cause authentication loops, where clients are forced into repeated refresh cycles without valid proofs.

Takeaways

DPoP significantly enhances OAuth 2.0 security by binding tokens to specific clients via cryptographic proof. Unlike mTLS, which relies on infrastructure-level certificate validation, DPoP operates at the application layer, offering greater flexibility for diverse client types. The key benefit is per-request validation of method and URI, preventing token misuse. Implementing sender constraints through the cnf claim is essential for protecting sensitive APIs against token theft.

FAQ

How does DPoP handle token refresh? When refreshing an access token, the client must present a new DPoP proof. The Authorization Server typically returns a new nonce value, which the client must include in subsequent proofs. This ensures that even if a refresh token is compromised, the attacker cannot easily replay old DPoP proofs without the corresponding private key.

Can DPoP be used with OAuth 2.1? Yes, DPoP is fully compatible with OAuth 2.1. In fact, OAuth 2.1 recommends DPoP or mTLS as the primary methods for sender-constraining access tokens, moving away from the implicit grant and other less secure flows. DPoP provides the necessary cryptographic binding required by the newer specification.

What happens if the client loses its private key? If the client loses its private key, it can no longer generate valid DPoP proofs. The client must generate a new key pair and re-register it with the Authorization Server (or obtain a new token with the new public key embedded in the cnf claim). Until the new key is established, the client will be unable to access protected resources.

Related posts