DPoP in Keycloak: sender-constrained tokens explained

In short
  • 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:

  1. The client generates a key pair and keeps the private key.
  2. With each token request, it sends a signed DPoP proof containing its public key.
  3. Keycloak issues a token bound to that key, recorded in the token's cnf.jkt claim.
  4. 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_jkt parameter 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:

CheckWhy
Proof signature matches the token's cnf.jktProves the caller holds the key
htm and htu match the requestStops the proof being reused on another endpoint
ath matches the access token hashTies the proof to this specific token
iat is recent and jti is uniqueLimits 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.

Talk to a Keycloak specialist

The first conversation is free.

Make an enquiry