Skip to content

DPoP

DPoP (Demonstrating Proof of Possession, RFC 9449) binds access tokens to a client’s cryptographic key pair, preventing token theft and replay attacks. Even if an access token is intercepted, it cannot be used without the corresponding private key.

The DPoP API is available at client.dpop only when the configured CryptoProvider implements the optional DPoP extension.

Initialize DPoP before making authentication requests:

const dpop = client.dpop;
if (!dpop) {
throw new Error('DPoP is not supported by this CryptoProvider');
}
await dpop.initialize();
if (dpop.isInitialized()) {
console.log('DPoP ready');
console.log('Thumbprint:', dpop.getThumbprint());
}

The SDK generates an asymmetric key pair and stores it for subsequent proof generation.

Generate a proof JWT for each HTTP request:

const proof = await dpop.generateProof('POST', 'https://auth.example.com/token');

For resource requests that include an access token, pass the token to generate the ath (access token hash) claim:

const accessToken = await client.token.getAccessToken();
const tokenHash = await dpop.calculateAccessTokenHash(accessToken);
const proof = await dpop.generateProof('GET', 'https://api.example.com/data', {
accessTokenHash: tokenHash,
});
ParameterTypeDescription
accessTokenHashstringBase64url-encoded SHA-256 hash of the access token
noncestringServer-provided nonce value

Authorization servers may require a nonce in DPoP proofs. When the server returns a DPoP-Nonce header, pass it to the SDK:

// Server responds with DPoP-Nonce header
dpop.handleNonceResponse(nonceFromServer);
// Subsequent proofs will include the nonce
const proof = await dpop.generateProof('POST', 'https://auth.example.com/token');

Verify that the authorization server supports DPoP:

const supported = await dpop.isServerSupported();
if (supported) {
await dpop.initialize();
}

This checks the dpop_signing_alg_values_supported field in the OIDC Discovery document.

Each proof is a signed JWT with the following structure:

{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
}
{
"jti": "unique-id",
"htm": "POST",
"htu": "https://auth.example.com/token",
"iat": 1699876543,
"ath": "base64url-sha256-of-access-token",
"nonce": "server-provided-nonce"
}
ClaimTypeDescription
jtistringUnique proof identifier (prevents replay)
htmstringHTTP method (uppercase)
htustringHTTP URL (scheme + host + path, no query)
iatnumberIssued at timestamp
athstringAccess token hash (when using access token)
noncestringServer-provided nonce (when required)
const client = await createAuthrimClient({
issuer: 'https://auth.example.com',
clientId: 'my-app',
crypto: cryptoProvider, // Must implement DPoPCryptoProvider
storage: storageProvider,
http: httpClient,
dpop: {
tokenRequests: true,
algorithm: 'ES256',
},
});
const dpop = client.dpop;
if (!dpop) {
throw new Error('DPoP is not supported by this CryptoProvider');
}
// Initialize DPoP when the server advertises support.
if (await dpop.isServerSupported()) {
await dpop.initialize();
}
// Build authorization URL (DPoP is transparent to the auth flow)
const { url } = await client.buildAuthorizationUrl({
redirectUri: 'https://myapp.com/callback',
});
// After callback, tokens are automatically DPoP-bound
const tokens = await client.handleCallback(callbackUrl);
// With dpop.tokenRequests enabled, token endpoint requests include DPoP proofs.
// If the server issues a DPoP-bound token, token_type is 'DPoP'.
const accessToken = await client.token.getAccessToken();

To reset the DPoP key pair (e.g., on logout):

await dpop.clear();

Retrieve the current DPoP public key:

const jwk = dpop.getPublicKeyJwk();
console.log(jwk); // { kty: 'EC', crv: 'P-256', x: '...', y: '...' }
const thumbprint = dpop.getThumbprint();
console.log(thumbprint); // JWK thumbprint string
Error CodeDescriptionRecovery
dpop_key_generation_errorFailed to generate the key pairCheck CryptoProvider implementation
dpop_proof_generation_errorFailed to sign the proof JWTCheck key availability, reinitialize
  • PAR — Pushed Authorization Requests
  • JAR & JARM — Signed authorization requests and responses
  • Token Management — Token lifecycle management