Theo Zourzouvillys

Field Note 6 current

Bind tokens to a key: sender-constrained tokens (DPoP)

By
Theo Zourzouvillys
Published
Tags
securityauthhttp

TL;DR

A bearer token — a session cookie, a JWT, an API key, a workload-identity token — grants access to whoever bears it. If it leaks, it’s replayable from anywhere until it expires. Sender constraint fixes that: bind the token to a holder key so that using it requires proving possession of a private key the token names. The standard mechanism is DPoP (RFC 9449RFC 9449 — OAuth 2.0 Demonstrating Proof of Possession (DPoP)Defines DPoP: sender-constraining OAuth tokens by binding them to a public key the client holds. Each request carries a short JWT proof signed by the matching private key, covering the method, URI, and a hash of the token — so a stolen token is useless without the key.rfc-editor.org ↗): the token carries a confirmation claim (cnf.jkt) naming the holder’s public-key thumbprint, and each request includes a short, signed DPoP proof over the method, URI, and a hash of the token. A token without its matching private key is useless. Default to sender-constraining any token whose theft would matter — especially long-lived workload credentials and anything validated at a trust boundary like a gateway.

Context

Almost everything authenticates with a bearer token, and the defining property is right there in the name: the token grants access to whoever holds it, with no binding to who you are or what you’re asking for. So if a token leaks — through an XSS bug, a logged Authorization header, a compromised proxy, a backup left in object storage — an attacker can replay it from anywhere and do anything the legitimate holder could, until it expires or someone notices.

Short token lifetimes reduce the window but don’t close it. The structural fix is to stop the token from being useful on its own.

Two complementary techniques address this, and they solve slightly different problems. This note is about the first; the second has its own note:

  • Sender-constrain the token (DPoP). Bind the credential to a key the client holds, so presenting the token also requires a fresh proof of possession. That’s this note.
  • Sign the message itself (HTTP Message Signatures). Sign the actual request — and ideally the response — so the recipient can prove who sent this message and that not a byte changed in flight. See ZFN-7. DPoP and message signing share a goal — kill pure-bearer replay — so pick by what you’re protecting (the token, or the message).

Recommendation

Sender-constrain any token whose theft would matter. Concretely, using DPoP:

  • Name the holder key in the token. When the token is issued, the client presents a public key; the issuer embeds its thumbprint in a confirmation claim (cnf.jkt). The token is now bound to that key.
  • Require a proof of possession on every request. The client sends a DPoP header: a short JWT, signed by the holder’s private key, that covers the HTTP method, the target URI, a timestamp/ nonce (anti-replay), and a hash of the access token (ath). The server verifies the proof’s signature against the key named in the token, and that the method/URI/token-hash match the actual request.
  • Validate it at the boundaries that matter. Verifying possession at the front door (an edge gateway) and at sensitive services keeps the credential sender-constrained end to end, not just at issuance.
  • Hold the holder key carefully. The whole guarantee rests on the private key staying private. A long-lived client that keeps the key resident (e.g. minting proofs on a hot path) must bound key residency and handle rotation/eviction; never log it.

The point is narrow and worth stating plainly: a stolen token, without the matching private key, buys the attacker nothing.

Consequences

Easier:

  • Token theft stops being game-over. A leaked token (logs, a proxy, a backup) isn’t replayable without the holder key.
  • Pairs cleanly with short-lived, platform-issued identity tokens (ZFN-5) to give theft resistance uniformly.

Harder:

  • Clients must hold a private key and sign every request — more moving parts than attaching a bearer string, and a key-management responsibility on the client side.
  • Proof verification adds work on the hot path (a signature check per request), and clock-skew/nonce handling needs care to avoid both replay windows and false rejections.
  • It constrains theft of the token, not compromise of the client that holds the key. If the attacker has the private key, they have the credential — sender constraint raises the bar, it doesn’t remove the need to protect the key.

New obligations:

  • Decide, per credential, whether its theft matters enough to sender-constrain — and default to “yes” for long-lived and high-value tokens.
  • Where you hold holder keys, document and bound their custody (residency, rotation, eviction, no-logging).

References

Changelog

  • 2026-06-12: First published as a Field Note.