DPoP Validation Options
The Duende.AspNetCore.Authentication.JwtBearer library adds DPoP proof token
validation to the ASP.NET Core JWT bearer authentication handler. This page describes how to register the library
and the options you can use to configure it. For background on why and when to validate DPoP tokens, see
Validating Proof-of-Possession.
Installation And Setup
Section titled “Installation And Setup”dotnet add package Duende.AspNetCore.Authentication.JwtBearerCall ConfigureDPoPTokensForScheme with the name of an existing JWT bearer authentication scheme. Replay detection is
enabled by default and requires a keyed HybridCache registration (see Replay Detection):
using Duende.AspNetCore.Authentication.JwtBearer.DPoP;
builder.Services.AddAuthentication("token") .AddJwtBearer("token", options => { options.Authority = "https://demo.duendesoftware.com"; options.TokenValidationParameters.ValidateAudience = false; options.MapInboundClaims = false; options.TokenValidationParameters.ValidTypes = ["at+jwt"]; });
// layers DPoP validation onto the "token" schemebuilder.Services.ConfigureDPoPTokensForScheme("token", options =>{ options.AllowBearerTokens = false; options.ProofTokenExpirationMode = DPoPProofExpirationMode.IssuedAt;});
// cache used for DPoP proof replay detectionbuilder.Services.AddKeyedHybridCache(ServiceProviderKeys.ProofTokenReplayHybridCache);DPoPOptions are named options, keyed by the authentication scheme name. If you use the overload without a
configuration delegate, you can still configure the options for that scheme later:
builder.Services.ConfigureDPoPTokensForScheme("token");builder.Services.Configure<DPoPOptions>("token", options =>{ options.ProofTokenLifetime = TimeSpan.FromSeconds(10);});DPoPOptions
Section titled “DPoPOptions”| Option | Default | Description |
|---|---|---|
AllowBearerTokens | false | When true, the scheme accepts both Bearer and DPoP access tokens. When false, only DPoP tokens are accepted. Enable this during a migration period where not all clients use DPoP yet. |
ProofTokenLifetime | 5 seconds | How long a proof token is considered valid, measured from its iat claim and/or the server-issued nonce (see ProofTokenExpirationMode). |
ProofTokenExpirationMode | DPoPProofExpirationMode.IssuedAt | Controls how proof token expiration is validated. See Proof Token Expiration. |
ProofTokenIssuedAtClockSkew | 25 seconds | Clock skew tolerance applied when validating the client-supplied iat claim. Since the iat value comes from the client’s clock, this tolerance is relatively large. |
ProofTokenNonceClockSkew | 5 seconds | Clock skew tolerance applied when validating the server-issued nonce. The nonce is created by the API itself, so only skew between API instances needs to be accounted for. |
ProofTokenMaxLength | 4000 | Maximum allowed length (in characters) of a DPoP proof token. Longer proofs are rejected to prevent resource-exhaustion attacks. |
ProofTokenValidationParameters | See below | The TokenValidationParameters used to validate the proof token JWT. |
EnableReplayDetection | true | Caches the jti of each proof token and rejects proofs that were already used. Requires a keyed HybridCache registration. See Replay Detection. |
ProofTokenValidationParameters
Section titled “ProofTokenValidationParameters”By default, proof tokens are validated with the following settings:
ValidTypesis set todpop+jwt.- Audience and issuer validation are disabled, as a DPoP proof does not contain these.
- Lifetime validation is disabled, as expiration is validated separately using the
iatclaim, a server-issued nonce, or both. ValidAlgorithmsallowsRS256,RS384,RS512,PS256,PS384,PS512,ES256,ES384andES512.
You can modify these, for example to restrict the allowed signing algorithms:
builder.Services.ConfigureDPoPTokensForScheme("token", options =>{ options.ProofTokenValidationParameters.ValidAlgorithms = [ SecurityAlgorithms.EcdsaSha256 ];});Proof Token Expiration
Section titled “Proof Token Expiration”The ProofTokenExpirationMode option accepts one of the following DPoPProofExpirationMode values:
IssuedAt(default): expiration is validated using theiatclaim in the proof token, allowing forProofTokenIssuedAtClockSkew. This requires no extra round-trips, but relies on the client’s clock being reasonably accurate.Nonce: expiration is validated using a nonce issued by the API, allowing forProofTokenNonceClockSkew. When a proof has no nonce, or the nonce is invalid or expired, the API responds with a401including ause_dpop_nonceerror and a fresh nonce in theDPoP-Nonceresponse header. The client must retry the request with a new proof containing that nonce. This removes the dependency on the client’s clock, at the cost of an extra round-trip.Both: both theiatclaim and the server-issued nonce are validated.
Client libraries such as Duende.AccessTokenManagement handle the nonce retry automatically.
Nonces And Load Balancing
Section titled “Nonces And Load Balancing”The default nonce implementation encodes the issue time using ASP.NET Core Data Protection. When you run multiple instances of your API behind a load balancer, make sure Data Protection keys are shared between instances, or nonces created by one instance will be rejected by another.
Replay Detection
Section titled “Replay Detection”When EnableReplayDetection is true (the default), the jti of every accepted proof token is stored in a cache for
the proof token lifetime plus clock skew, and proofs that reuse a jti are rejected.
The cache is resolved as a keyed HybridCache service, using the ServiceProviderKeys.ProofTokenReplayHybridCache
key. If replay detection is enabled and no such cache is registered, an InvalidOperationException is thrown when a
DPoP proof is validated. Register the cache as follows:
builder.Services.AddKeyedHybridCache(ServiceProviderKeys.ProofTokenReplayHybridCache);HybridCache uses an in-memory cache by default, and also uses an IDistributedCache as a second-level cache when one
is registered. When your API runs on multiple instances, register a distributed cache (for example, Redis) so replayed
proofs are detected across instances. See the
Microsoft documentation on HybridCache
for details.
If you do not need replay detection, disable it:
builder.Services.ConfigureDPoPTokensForScheme("token", options =>{ options.EnableReplayDetection = false;});