Back to HTTP Headers

DPoP-Nonce auth both

A server-issued challenge value that must be embedded in subsequent DPoP proofs, adding freshness guarantees beyond timestamp checking alone.

What it does

DPoP-Nonce is a server-issued, single-use challenge value that authorization servers and resource servers can require clients to embed in their next DPoP proof. It exists to close a gap plain timestamp-based freshness checking (the iat claim in a DPoP JWT) leaves open: without a server-provided nonce, an attacker with a narrow window of opportunity could still theoretically pre-generate and use a DPoP proof within its valid timestamp range. Requiring a server-issued nonce that changes per-challenge makes proofs meaningfully harder to precompute or reuse.

Syntax

Server issues a nonce (as a response header):

DPoP-Nonce: eyJhbGciOiJIUzI1NiJ9.opaque-nonce-value

Client includes it in the next DPoP proof's payload:

{
  "jti": "unique-request-id",
  "htm": "POST",
  "htu": "https://api.example.com/resource",
  "iat": 1723999999,
  "nonce": "eyJhbGciOiJIUzI1NiJ9.opaque-nonce-value"
}

The nonce challenge-response flow

  1. Client sends a request with a DPoP proof, but without a nonce (or with an expired/incorrect one).
  2. Server rejects the request, responding 400 Bad Request and issuing a fresh nonce via the DPoP-Nonce response header.
  3. Client generates a new DPoP proof, this time including the server-provided nonce in its payload, and retries the request.
  4. Server validates the proof (including the nonce matches what it issued) and processes the request normally.
  5. The server may periodically rotate the required nonce, triggering the same challenge-response cycle again.

This means a client implementing DPoP correctly needs to handle an extra round trip on the first request (or whenever the nonce rotates) — something worth designing for rather than treating as an unexpected error case.

Common mistakes and gotchas

Not implementing the retry-with-nonce flow. A client that doesn't recognize the 400 + DPoP-Nonce response pattern and automatically retry with the nonce embedded will simply fail to authenticate against servers that require nonces — this is a specific flow that needs deliberate client-side handling, not something that happens automatically just by sending a DPoP proof.

Treating nonce support as optional when the server requires it. Whether a nonce is required is up to the specific authorization/resource server's policy — some require it, some don't. Client implementations need to handle both cases (server that never issues a nonce, and server that does and expects it in every subsequent proof) rather than assuming one universal behavior.

Caching or reusing a nonce beyond its validity. Nonces are meant to be used for a limited time/number of requests before the server rotates them — continuing to use a stale nonce will eventually trigger a fresh 400 + new nonce challenge, so clients should be prepared to handle repeated rotation throughout a session, not just once at the start.

Confusing this with a CSRF token or session nonce. DPoP-Nonce serves a narrow, specific purpose within the DPoP proof-of-possession mechanism — it's not a general-purpose anti-CSRF mechanism and shouldn't be conflated with unrelated nonce-based security patterns used elsewhere in an application.

Real-world examples

Initial request rejected, server issues a nonce:

POST /token HTTP/1.1
DPoP: eyJ0eXAiOiJkcG9wK2p3dCJ9...(no nonce claim)

HTTP/1.1 400 Bad Request
DPoP-Nonce: xyz-fresh-nonce-value
{"error": "use_dpop_nonce"}

Client retries with the nonce embedded, succeeds:

POST /token HTTP/1.1
DPoP: eyJ0eXAiOiJkcG9wK2p3dCJ9...(includes "nonce": "xyz-fresh-nonce-value")

HTTP/1.1 200 OK

FAQ

Do all DPoP implementations require a nonce?

No — nonce support is at the discretion of the specific authorization/resource server. A client implementing DPoP needs to gracefully handle both servers that require nonces and ones that don't.

What happens if my client doesn't handle the nonce challenge-response correctly?

Requests to servers requiring a nonce will keep failing with 400 Bad Request and an use_dpop_nonce error, since the client never retries with the required nonce embedded in its proof — this looks like a persistent authentication failure rather than an obvious "you forgot a header" error.

How often does the required nonce change?

This is entirely up to server policy and isn't fixed by the spec — some servers rotate nonces per-request, others less frequently. Client implementations should be resilient to unexpected rotation at any point, not assume a fixed refresh interval.

Is DPoP-Nonce sent on every single request?

The server only issues it via the DPoP-Nonce response header when it wants to challenge or rotate the nonce — clients then include the current known nonce value in their DPoP proof payload on subsequent requests, not necessarily on every single response/request pair.

Fun fact

The nonce mechanism in DPoP mirrors a much older security pattern from HTTP Digest Authentication (RFC 7616), which also used server-issued nonces to prevent replay attacks decades before DPoP existed. It's a good example of a proven cryptographic freshness pattern getting reapplied to solve a structurally similar problem in a completely different, more modern authentication context — proof that some security design patterns remain fundamentally sound even as the surrounding protocols evolve dramatically.

Related Headers