DPoP (RFC 9449) ties an access token to a key pair held by the client. The client signs a short-lived proof JWT for each request. A token that leaks from a log or a proxy is useless without the private key that signed the proofs.
This page covers the authorization server side and the matching resource server option. For the resource server itself, see Resource Server.
DPoP is off unless Config.AuthorizationServer.DPoP is set. An empty &theauth.DPoPConfig{} is valid and applies the defaults below.
How the token endpoint binds a token
POST /oauth/token reads the DPoP request header. The handler passes the proof, the request method, and the request URL to the verifier, which checks the signature, htm, htu, iat, and jti replay.
- If the proof is valid, the access token gets an RFC 7800
cnf claim holding the RFC 7638 thumbprint of the proof key (cnf.jkt), and the response token_type is DPoP instead of Bearer.
- If no proof is sent and the client is not in
RequireDPoPForClients, a normal Bearer token is issued.
- If the nonce is missing or stale, the endpoint answers 400
use_dpop_nonce with a DPoP-Nonce header. The client retries with that nonce in the proof.
- Any other proof failure returns 400
invalid_dpop_proof.
The same binding applies to the authorization code, refresh token, and the other grants that go through the token endpoint. The AS metadata document advertises dpop_signing_alg_values_supported.
Verify on the resource server
Enable DPoP checks on the mcpresource validator with WithDPoPVerification:
The arguments are the allowed algorithms (nil or empty uses the defaults), the proof max age, and the jti replay window. For any token that carries cnf.jkt, the middleware requires a DPoP header whose proof key thumbprint matches, and the proof must be bound to the request method, URL, and access token hash. Tokens without cnf.jkt still pass as plain Bearer tokens, so the AS decides per token whether to constrain. After validation, Principal.CnfJKT holds the thumbprint.
Clients send Authorization: DPoP <token> together with the DPoP header.
Security notes
- Set
RequireDPoPForClients for public clients and agents. Opt-in only protects clients that choose to send a proof.
- Turn on
RequireNonceForTokens when untrusted clients can reach the token endpoint. It limits how long a captured proof stays usable.
- The
jti cache is in memory per process. Behind a load balancer, a replay can land on a different instance, so pair DPoP with short ProofMaxAge and nonces.
- Keep
NonceSecret out of source control and stable across instances.
Last modified on October 8, 2026