DPoP in Keycloak: sender-constrained tokens explained
- A normal bearer token works for anyone who holds it. A DPoP token only works with the client's private key.
- Keycloak has fully supported DPoP since version 26.4 (it was a preview feature from version 23).
- Turn it on per client with Require DPoP bound tokens, then make sure your APIs check the proof.
The problem: bearer tokens
Most OAuth access tokens are bearer tokens: whoever presents one gets in. If a token leaks through a log file, a compromised browser extension or a misconfigured proxy, an attacker can replay it until it expires.
What DPoP does
DPoP (Demonstrating Proof of Possession, RFC 9449) binds tokens to a key pair the client holds:
- The client generates a key pair and keeps the private key.
- With each token request, it sends a signed DPoP proof containing its public key.
- Keycloak issues a token bound to that key, recorded in the token's
cnf.jktclaim. - Every API call carries a fresh proof signed with the same key. No key, no access.
Enabling DPoP in Keycloak
In the admin console, open the client, go to Settings → Capability config and enable Require DPoP bound tokens. Without this switch, a client that sends a proof still gets a bound token, but binding isn't enforced.
Keycloak 26.4 also added:
- Binding only refresh tokens for public clients, if your APIs can't validate DPoP yet
- DPoP support on Keycloak's own endpoints, including the Admin REST API and Account API
- The
dpop_jktparameter in authorisation requests - FAPI 2.0 client profiles for DPoP clients
Don't forget the resource server
Keycloak issuing DPoP tokens is only half the job. Each API must validate the proof on every request:
| Check | Why |
|---|---|
Proof signature matches the token's cnf.jkt | Proves the caller holds the key |
htm and htu match the request | Stops the proof being reused on another endpoint |
ath matches the access token hash | Ties the proof to this specific token |
iat is recent and jti is unique | Limits replay of the proof itself |
Browser apps
For single-page apps, generate the key pair with the Web Crypto API as non-extractable, so script running on the page can use the key but never read it out. That's what makes DPoP worthwhile in the browser, where token theft is most likely.
DPoP FAQ
Which Keycloak version supports DPoP?
DPoP was a preview feature from Keycloak 23 and became fully supported in Keycloak 26.4.
Does DPoP replace short token lifetimes?
No. DPoP works alongside short-lived tokens and refresh token rotation, not instead of them.
Rolling out DPoP? Julio contributed DPoP support to keycloak-js. Talk to us about bringing sender-constrained tokens to your apps and APIs.