Advanced Token Security (DPoP, mTLS, PAR, JAR, FAPI)
When to Use This Skill
- Implementing Proof-of-Possession (PoP) tokens with DPoP or mTLS
- Configuring Pushed Authorization Requests (PAR) for front-channel parameter security
- Setting up JWT Secured Authorization Requests (JAR) for tamperproof authorize requests
- Building FAPI 2.0 compliant authorization servers
- Choosing between DPoP and mTLS for sender-constrained tokens
- Configuring APIs to validate proof-of-possession tokens
- Meeting regulatory or industry security requirements (open banking, e-health, e-government)
Docs: https://docs.duendesoftware.com/identityserver/tokens/
Proof-of-Possession Tokens: Why They Matter
Default OAuth access tokens are bearer tokens -- anyone who possesses the token can use it. If a token leaks, a malicious third party can impersonate the client/user.
Proof-of-Possession (PoP) tokens are cryptographically bound to the client that requested them via the cnf (confirmation) claim:
When using reference tokens, the cnf claim is returned from the introspection endpoint.
DPoP vs mTLS: Decision Matrix
Mutual TLS (mTLS)
How It Works
IdentityServer embeds the SHA-256 thumbprint of the client's X.509 certificate into the access token via the cnf claim:
The client must use the same certificate when calling APIs. APIs validate the cnf claim against the TLS client certificate thumbprint.
mTLS for Client Authentication
Configure IdentityServer to accept client certificates:
Configure the client with certificate-based secrets:
Use SecretTypes.X509CertificateName for PKI/chained certificates (matched by distinguished name) and SecretTypes.X509CertificateThumbprint for self-issued certificates (matched by thumbprint).
mTLS Endpoint URL Strategies
options.MutualTls.DomainName controls where the mTLS-protected endpoints live:
The mTLS endpoint URLs are published in discovery under mtls_endpoint_aliases. Clients must read them from there rather than the standard endpoints:
mTLS Deployment: Kestrel (dev) vs Reverse Proxy (production)
Development — Kestrel terminates TLS directly. Use mkcert to create a locally-trusted CA (the private key stays on your machine, unlike shared sample certificates that ship public private keys). Accept — but don't require — client certificates; IdentityServer's mTLS middleware enforces them on the mTLS endpoint:
On .NET 10, Kestrel auto-resolves
*.localhostsub-domains, so themtls.localhostsub-domain strategy works locally with no hosts-file entry.
Production — a reverse proxy terminates TLS and forwards the client certificate to Kestrel via a request header. Kestrel itself does not negotiate the certificate (ClientCertificateMode.NoCertificate); use certificate forwarding:
Proxy header conventions:
Security: the proxy must strip or overwrite the certificate header on all inbound requests so a client cannot spoof a certificate by sending the header directly.
mTLS without Client Authentication
You can bind tokens to a client certificate without using the certificate for client authentication. This works with any authentication method, including public clients:
The client creates a certificate on the fly and uses it to establish the TLS channel:
.NET Client Requesting mTLS Token
Validating mTLS in APIs
Add custom middleware to compare the cnf claim against the TLS client certificate:
The middleware validates the x5t#S256 value in the cnf claim against the SHA-256 thumbprint of the client certificate on the TLS channel.
DPoP (Demonstrating Proof-of-Possession at the Application Layer)
Version: >= 6.3 (Enterprise Edition)
DPoP binds an asymmetric key (stored as a JWK) to an access token via the cnf claim:
The client proves possession of the private key by sending a signed JWT (proof token) via the DPoP HTTP header on every request.
Enabling DPoP in IdentityServer
DPoP can be used dynamically with no server configuration, or enforced per-client:
Client-Side DPoP Configuration
Use Duende.AccessTokenManagement for automatic DPoP proof token handling.
Client credentials flow:
Authorization code flow:
Creating a DPoP JWK
The DPoPJsonWebKey is a critical secret. If lost, tokens bound to it cannot be used. If leaked, the security benefits of DPoP are lost.
DPoP Client Settings Reference
Validating DPoP in APIs
Install the DPoP validation package:
Configure JWT bearer with DPoP:
DPoP validation requires a distributed cache for replay detection:
DPoP Validation Steps (handled by the library)
- Validate the access token as normal
- Validate the DPoP proof token from the
DPoPHTTP request header - Ensure the authorization header uses the
DPoPscheme - Validate the JWT format of the proof token
- Verify the
cnfclaim matches between tokens - Validate the HTTP method and URL match the request
- Detect replay attacks using distributed cache storage
- Manage nonce generation and validation
- Handle clock skew between systems
- Return appropriate error response headers when validation fails
Pushed Authorization Requests (PAR)
Version: >= 7.0 (Business and Enterprise Edition)
PAR moves authorization parameters from the front channel (browser redirect URLs) to the back channel (direct HTTP POST), preventing parameter leakage and tampering.
Why PAR
- Prevents exposure of authorization parameters (PII in scopes, claims)
- Prevents tampering with parameters (attacker changing scope)
- Keeps request URLs short (avoids browser/infrastructure URL length limits)
- Required by FAPI 2.0 Security Profile
Server Configuration
Per-Client Configuration
Client Usage (.NET 9+)
Disabling PAR (Starter Edition)
PAR requests are not processed in the Starter edition. Disable the endpoint to reflect this in discovery:
PAR Configuration Reference
JWT Secured Authorization Requests (JAR)
JAR packages authorization request parameters in a signed JWT, making them tamperproof and enabling front-channel client authentication.
Server Configuration
Configure the client to require signed request objects:
The same key can be shared between client authentication (private_key_jwt) and signed authorize requests.
Request JWTs by Reference
If using request_uri, IdentityServer fetches the JWT from the specified URL:
Request URI processing is disabled by default. Enable it on the Endpoints options.
Accessing Request Object Data
- In
ValidatedAuthorizeRequest: use theRequestObjectValuesdictionary - In UI code: call
IIdentityServerInteractionService.GetAuthorizationContextAsync, then accessRequestObjectValueson the returnedAuthorizationRequest
FAPI 2.0 Compliance
Version: >= 7.3 (Enterprise Edition)
The FAPI 2.0 Security Profile is a set of OAuth security best practices for high-value scenarios (open banking, e-health, e-government).
FAPI 2.0 Authorization Server Requirements Checklist
FAPI 2.0 Server Setup
FAPI 2.0 Client Configuration
FAPI 2.0 API Configuration
FAPI 2.0 HTTP Redirects
Starting in v8.0, IdentityServer unconditionally uses HTTP 303 (See Other) redirects from POST endpoints, in compliance with FAPI 2.0 Section 5.3.2.2.
Private Key JWT vs mTLS for FAPI 2.0
Start with private key JWTs. mTLS is relatively challenging to maintain in production. Both are supported and FAPI 2.0 compliant.
Edition Requirements Summary
Common Pitfalls
-
Using
ClientCredentialStyle.AuthorizationHeaderwith mTLS - The defaultAuthorizationHeaderstyle does not work in mTLS scenarios. UseClientCredentialStyle.PostBodyinstead. -
Missing distributed cache for DPoP - DPoP replay detection requires
IDistributedCache. Without it, replay attacks are possible. Use Redis, SQL Server, or another durable cache in production. -
PAR lifetime too short - The default 10 minutes balances security (FAPI 2.0 recommendation) with usability. If users take longer to authenticate (MFA, consent), increase the lifetime.
-
DPoP key management - The
DPoPJsonWebKeymust persist for the lifetime of tokens bound to it. Losing the key makes bound tokens unusable. Leaking it nullifies DPoP's security benefits. -
Confusing DPoP with client authentication - DPoP proves token possession at the application layer. It is separate from client authentication (which proves client identity at the token endpoint). A client can use shared secrets for authentication and DPoP for token binding.
-
Not enabling
AlwaysEmitConfirmationClaimfor mTLS without mTLS auth - If you want certificate binding without certificate-based client authentication, you must setMutualTls.AlwaysEmitConfirmationClaim = true. -
Forgetting to configure DPoP proof validation algorithms - For FAPI 2.0, explicitly set
ProofTokenValidationParameters.ValidAlgorithmson the API side. Without this, the API may accept weaker algorithms. -
PAR not available in Starter edition - PAR requests are rejected in the Starter edition. Disable the endpoint via
options.Endpoints.EnablePushedAuthorizationEndpoint = falseso discovery accurately reflects this.


