DPoP-Nonce
Replay attacks against DPoP-bound tokens need server-side mitigation. The HTTP DPoP-Nonce response header provides a server-generated nonce for clients to include in subsequent DPoP proof JWTs, binding each proof to a recent server interaction.
Usage
When an authorization or resource server requires stronger replay protection
for DPoP-bound tokens, the server sends a DPoP-Nonce header in its
response. The client extracts this nonce value and includes the value in the
nonce claim of the next DPoP proof JWT sent to the same server.
Nonce validation is an optional extension to the core
DPoP protocol. Servers use nonces when the unique identifier (jti claim)
and timestamp (iat claim) in the DPoP proof JWT provide insufficient
protection against replay attacks. By requiring a fresh nonce from the server
before accepting a proof, the server ensures the proof was generated recently
and specifically for the current interaction.
Servers send the DPoP-Nonce header in responses to both successful and failed requests. When a client sends a DPoP proof without a required nonce or with an expired nonce, the server rejects the request and provides a fresh nonce in the response. The client retries the request with a new DPoP proof containing the fresh nonce.
Nonce values are opaque to the client. The server generates and validates nonces using any technique appropriate to its security requirements. Common approaches include time-based tokens, cryptographic MACs, or database-backed unique identifiers.
Values
The header value is an ASCII string generated by the server. The specification does not define a format. Servers design nonce values to support their validation logic while remaining opaque to clients.
Example
An authorization server responding to a token request. The client included a valid DPoP proof, and the server issues the access token while also providing a nonce for use in subsequent requests to resource servers.
DPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v
A resource server rejecting a request because the DPoP proof lacked a
required nonce. The DPoP-Nonce header in the 401 Unauthorized
response provides the nonce needed for the retry. The client generates a new
DPoP proof containing this nonce value in the nonce claim.
HTTP/1.1 401 Unauthorized
DPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v
WWW-Authenticate: DPoP error="use_dpop_nonce", error_description="Resource server requires nonce in DPoP proof"
Why servers hand out nonces
Without a server nonce, a party holding the client generates proofs dated arbitrarily far ahead, and possession of a proof stops meaning possession of the key. The specification's own example is an insider: an employee pre-generating proofs on an audited machine for later use on an unaudited one. A server-chosen nonce inside each proof closes the window, and once a server has issued one, accepting proofs without a nonce is prohibited outright.
The challenge arrives differently per server role.
An authorization server answers 400 with the
use_dpop_nonce error code and a DPoP-Nonce
header. A resource server answers 401 with a
WWW-Authenticate challenge
naming the same error, plus the header. Nonces bind
to their issuing server, so a client talking
to several servers keeps a nonce per server rather
than one global value, and the OpenID Connect ID
token nonce is an unrelated mechanism sharing only
the name.
Rotation rides on success. A 200 carrying a new DPoP-Nonce hands the client its next value without the round trip a fresh challenge costs, and responses carrying the header belong outside caches.
Browser clients hit one recurring wall: the header is invisible to script until the server lists DPoP-Nonce in Access-Control-Expose-Headers, the most common single-page-application failure with DPoP.
Rotation policy is the deployment's choice. Okta rotates daily and honors old nonces for three days, while Bluesky's AT Protocol mandates rotation within five minutes, both conformant, since the specification sets no lifetime.
See also
- RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP)
- DPoP
- Authentication
- Authorization
- HTTP headers