A bearer access token normally authorizes whichever party can present its value to a resource server. Copying the token can therefore move its authority away from the client that originally received it. OAuth 2.0 Demonstrating Proof of Possession, or DPoP, changes that property by binding a token to a public key and requiring a signed proof from the corresponding private key during presentation.

That binding narrows one important failure mode, but it does not turn the key into a universal client identity. DPoP is an application-layer sender-constraining mechanism. Its guarantees depend on the token binding, proof validation, replay policy, TLS, and the security of the client execution context.

The access token carries a key binding

RFC 9449 defines a DPoP proof as a signed JWT sent in the DPoP HTTP header. During token issuance, the authorization server can bind the resulting access token to the public key represented by the proof.

For a JWT access token, the binding can appear in the cnf claim through the jkt member. jkt is the base64url-encoded SHA-256 JWK Thumbprint of the DPoP public key.

{
  "cnf": {
    "jkt": "public-key-thumbprint"
  }
}

An opaque token can carry the same relationship indirectly. A resource server can obtain the cnf metadata through token introspection and perform the binding check locally.

The security property is narrower than ownership of a bearer token. Possession of the token value alone is insufficient when the resource server enforces DPoP correctly; the presenter also needs a valid proof from the bound key.

A proof is tied to a particular HTTP request

A DPoP proof includes request context rather than acting as a reusable signature over the access token. Its protected JOSE header identifies the proof type, signing algorithm, and public JWK. The JWT payload includes claims such as jti, htm, htu, and iat.

For protected-resource access, the proof also contains ath, which is derived from the presented access token.

DPoP proof
  |
  +-- htm   HTTP method
  +-- htu   target URI
  +-- iat   creation time
  +-- jti   proof identifier
  +-- ath   SHA-256 hash of access token

The resource server verifies the signature and checks that htm matches the request method and htu matches the target URI according to RFC 9449 rules. It also computes the access-token hash and compares it with ath, then confirms that the proof key matches the key to which the token is bound.

These checks serve different purposes. htm and htu constrain the proof to request context. ath prevents a proof created for one access-token value from being paired with another token. The cnf.jkt binding connects the token to the proof key.

Skipping one of these checks changes the resulting security property.

Sender constraint is not client authentication

DPoP does not define client authentication. A public OAuth client can use DPoP without holding a conventional client secret, and a confidential client can combine DPoP with a separate authentication method.

This distinction matters when authorization policy refers to an application identity. A valid DPoP proof establishes possession of the private key corresponding to the public key in the proof. The token binding then establishes that this is the key associated with the token. Neither statement alone proves a deployment-specific client identity unless another mechanism or policy establishes that relationship.

The same separation applies to user identity. A DPoP key is not a substitute for the subject and authorization semantics carried by the OAuth grant and resulting token.

Replay resistance depends on server policy

A signed proof is still data that can be copied. RFC 9449 requires freshness checks and defines jti as a unique proof identifier, but replay handling has operational choices. A server can track proof identifiers within an acceptance window, and it can require a server-provided nonce.

A nonce challenge adds server-selected freshness:

client                       resource server
  |                                |
  | request + DPoP proof           |
  |------------------------------->|
  | 401 + DPoP-Nonce               |
  |<-------------------------------|
  | request + new proof + nonce     |
  |------------------------------->|

The nonce is opaque to the client. Once a server has required a DPoP nonce, RFC 9449 prohibits accepting a proof that omits the required nonce. This prevents a downgrade from nonce-bound validation to a weaker proof policy.

Server-provided nonces also address proof pre-generation. Without an unpredictable server contribution, code controlling a client key can create proofs with future timestamps and move those proofs elsewhere. A fresh server nonce limits that strategy because the proof cannot be prepared before the nonce exists.

Nonce use does not remove the need for the other checks. The proof still needs valid request binding, token binding, signature verification, and acceptable freshness.

A non-exportable key does not neutralize hostile client code

Keeping the private key in a non-exportable keystore can prevent direct extraction of key material. That is valuable, but the distinction between extracting a key and using a key is critical.

If hostile code executes inside the client context and can invoke the signing operation, it may be able to produce valid DPoP proofs while the client remains available. RFC 9449 explicitly treats untrusted code in the client context as a limit on the mechanism’s protection.

The resulting boundary is:

key extraction blocked
        !=
key use blocked

A hardware-backed or non-exportable key can reduce portability of stolen credentials. It does not, by itself, make an execution context trustworthy. Controls against script injection, malicious dependencies, compromised processes, and unauthorized access to signing APIs remain separate.

TLS remains part of the security model

DPoP signs selected request information; it is not a general HTTP message-integrity protocol. RFC 9449 relies on TLS for transport protection.

For example, htu binds the proof to a target URI under the specification’s comparison rules, while htm binds it to the method. The proof does not cryptographically cover every request header and body byte. Applications must not infer full message integrity from the presence of a valid DPoP JWT.

TLS certificate validation also remains necessary. Sender-constraining an access token does not authenticate an arbitrary network peer as the intended authorization server or resource server.

Token theft and execution-context compromise remain different cases

DPoP is strongest when the attacker obtains an access-token value but not the associated private key or a usable signed proof. In that case, correct resource-server enforcement prevents the copied token from behaving like an ordinary bearer credential.

A stronger compromise changes the analysis. If an attacker can operate inside the client context, invoke the key, capture valid proofs, or control requests before signing, DPoP cannot restore the original trust boundary on its own.

This distinction is operationally important. Token storage, key storage, process isolation, browser script integrity, TLS validation, OAuth authorization policy, and DPoP enforcement are complementary controls. Treating the DPoP key as a universal identity or as a complete defense against client compromise assigns it properties the protocol does not provide.

The enforcement point is the resource server

The authorization server creates the token-to-key relationship, but protected-resource enforcement gives that relationship practical effect. A resource server that accepts a DPoP-bound token as a plain bearer token discards the sender constraint.

Correct enforcement therefore requires the resource server to validate both artifacts together: the access token and the DPoP proof. It must confirm token validity, proof validity, request binding, ath, and the public-key binding. If nonce policy is active, nonce validation joins that set.

DPoP shifts an OAuth access token from pure possession of a string toward possession of a string plus control of a particular signing key. That is a precise and useful boundary. Keeping the boundary precise also prevents it from being mistaken for client authentication, full request signing, or protection against hostile code already operating where the key can be used.