Skip to content
Introducing the next era of Duende IdentityServer. Read our CEO’s announcement

OIDC Client DPoP Support

import { LinkCard } from “@astrojs/starlight/components”;

DPoP (Demonstrating Proof of Possession) is an extension to OAuth 2.0 that provides proof-of-possession for access tokens. It binds tokens to a specific cryptographic key, preventing token theft and replay attacks.

DPoP support is provided by the Extensions package:

Terminal window
dotnet add package Duende.IdentityModel.OidcClient.Extensions

Use the JsonWebKeys helper to create a key pair:

using Duende.IdentityModel.OidcClient.DPoP;
// Create an RSA key (recommended for most scenarios)
var proofKey = JsonWebKeys.CreateRsaJson();
// Or create an ECDSA key (smaller, faster)
var proofKey = JsonWebKeys.CreateECDsaJson();
var options = new OidcClientOptions
{
Authority = "https://demo.duendesoftware.com",
ClientId = "native.dpop",
RedirectUri = "app://callback",
Scope = "openid profile api"
};
// Enable DPoP
options.ConfigureDPoP(proofKey);
var client = new OidcClient(options);
var loginResult = await client.LoginAsync();
if (!loginResult.IsError)
{
// Create a handler for DPoP-protected API calls
var handler = client.CreateDPoPHandler(
proofKey,
loginResult.RefreshToken
);
var apiClient = new HttpClient(handler);
var response = await apiClient.GetAsync("https://api.example.com/resource");
}

Helper class for generating DPoP proof keys:

MethodDescription
CreateRsa(algorithm)Creates an RSA JsonWebKey (default: PS256)
CreateRsaJson(algorithm)Creates an RSA key as JSON string
CreateECDsa(algorithm)Creates an ECDSA JsonWebKey (default: ES256)
CreateECDsaJson(algorithm)Creates an ECDSA key as JSON string

Configures the OidcClient to use DPoP for token requests:

// Using a JSON proof key string
options.ConfigureDPoP(proofKey);
// Using a custom proof token factory
options.ConfigureDPoP(
proofTokenFactory,
tokenEndpointInnerHandler, // Optional: custom handler for token endpoint
apiInnerHandler // Optional: custom handler for API calls
);

Creates an HTTP message handler for DPoP-protected API calls:

// Using a JSON proof key string
var handler = client.CreateDPoPHandler(proofKey, refreshToken);
// Using a custom proof token factory
var handler = client.CreateDPoPHandler(
proofTokenFactory,
refreshToken,
apiInnerHandler // Optional: custom inner handler
);

For advanced scenarios, implement IDPoPProofTokenFactory:

public class CustomProofTokenFactory : IDPoPProofTokenFactory
{
public DPoPProof CreateProofToken(DPoPProofRequest request)
{
// request.Url - The HTTP URL
// request.Method - The HTTP method (GET, POST, etc.)
// request.DPoPNonce - Server-provided nonce (if any)
// request.AccessToken - The access token (for ath claim)
var proofToken = // ... create JWT proof token
return new DPoPProof { ProofToken = proofToken };
}
}
PropertyTypeDescription
UrlstringThe HTTP URL of the request
MethodstringThe HTTP method (GET, POST, etc.)
DPoPNoncestringServer-provided nonce value
AccessTokenstringThe access token (for ath claim binding)

Low-level handler that adds DPoP proof tokens to requests:

var handler = new ProofTokenMessageHandler(
proofTokenFactory,
innerHandler,
logger // Optional: ILogger<ProofTokenMessageHandler>
);

The DPoPExtensions class provides helper methods:

MethodDescription
SetDPoPProofToken(request, proofToken)Adds the DPoP header to a request
GetDPoPNonce(response)Extracts the DPoP-Nonce from a response
GetDPoPUrl(request)Gets the URL for DPoP proof creation
  1. Generate keys on device - Create proof keys locally and store them securely
  2. Use appropriate key type - RSA (PS256) for broad compatibility, ECDSA (ES256) for performance
  3. Handle nonce requirements - The handler automatically handles server-provided nonces
  4. Persist keys securely - Use platform-specific secure storage