Skip to content

Sender constraint — DPoP vs mTLS ​

A bare bearer token can be used by whoever has its bytes. If the token leaks (logs, intermediary proxy, browser extension, third-party SDK) the leaker has full access until the token expires.

A sender-constrained access token is bound to a key the legitimate client holds. Even if the bytes leak, the leaker cannot use the token without also stealing the key. This page is the selection guide; the mechanics live on the dedicated pages for DPoP and mTLS.

Token replay — what is it?

A leaker captures a valid access token (e.g. from a log file or a compromised proxy) and re-sends it from their own machine to call the API. The RS sees a syntactically valid token and serves the request. Sender constraint blocks token-only replay — the leaker would also have to present the matching key on the same request.

Proof-of-possession — what is it?

The general term for "show me you hold the key, not just the token bytes." DPoP, mTLS, and the older holder-of-key tokens are all proof-of-possession schemes. The library's DPoP / mTLS flows are the modern OAuth-native expressions of this idea.

Why bearer tokens are risky in 2026 ​

Token leakage is no longer hypothetical:

  • Logs. Reverse-proxy access logs, application logs, and observability pipelines routinely capture the Authorization header unless explicitly stripped. A bearer token surviving in a log file is reusable for the rest of its TTL.
  • Browser extensions and SDKs. Modern browsers run extensions inside the same process boundary as the page; a malicious extension can read every header the page sets. Mobile SDKs sit in the same process as the app.
  • Compromised intermediaries. A single compromised CDN edge or proxy gains access to every request flowing through it. Bearer tokens are the most useful artefact to harvest.
  • Stage-and-fire. An attacker with brief access to a developer's machine can copy a token and use it later from anywhere on the internet.

The structural fix is to make the bytes alone insufficient. Sender constraint achieves this by tying every request to a key the legitimate client controls. The leak still happens; the token alone no longer compromises the API.

Sender constraint vs TLS

TLS protects the token while it is on the wire. Once the request reaches the application — the OP, the RS, a logging middleware, a debug endpoint — the bearer token is in plaintext and any of those points can leak it. Sender constraint protects against token-only replay after the token reaches those components.

Two ways to bind in this library ​

DPoP (RFC 9449) binds the token to a key the client signs with on every request. The proof is a small JWT (htm, htu, iat, jti, optional ath and nonce) attached as a DPoP: HTTP header. Works over plain HTTPS and does not require the client to hold a TLS certificate. Read DPoP for the full mechanics.

mTLS (RFC 8705) binds the token to the X.509 certificate the client presented during the TLS handshake. The OP records a SHA-256 thumbprint of the certificate as cnf.x5t#S256 on the issued token, and the resource server verifies that the cert presented to it has the same thumbprint. Read mTLS for the full mechanics.

Comparison ​

AspectDPoPmTLS
SpecRFC 9449RFC 8705
Key materialClient-held private key, any device that can signClient TLS certificate (PKI-issued or self-signed)
Browser supportYes (SPA, mobile, anything that can sign a JWT)Poor — browsers cannot easily present client certs
Per-request artefactFresh JWS proof, signed in the applicationNothing extra — the binding lives at the TLS layer
Proxy / TLS terminator dependencyNone — works over plain HTTPSRequires the terminator to forward the cert in a header
cnf membercnf.jkt (JWK thumbprint)cnf.x5t#S256 (X.509 thumbprint)
Refresh token binding (default)Bound for public clients, unbound for confidential (Design judgment #15)Access tokens can be certificate-bound; mTLS token-endpoint client authentication is not dispatched
Replay defense beyond bindingjti cache, iat window, optional server nonceTLS session reuse + cert thumbprint match
FAPI 2.0 Security Profile (confidential clients)YesYes
FAPI 2.0 Message SigningInherits the Security Profile; DPoP nonce is optionalInherits the Security Profile

The Final FAPI 2.0 Security Profile applies to confidential clients and requires sender-constrained tokens via one of these two methods; its Message Signing extension inherits that requirement and does not add a DPoP nonce requirement. This library accepts both for token binding. op.WithProfile(profile.FAPI2Baseline) imposes RequiredAnyOf over [feature.DPoP, feature.MTLS]. If neither is enabled, construction selects feature.DPoP as the default member; if you explicitly enable feature.MTLS, that choice satisfies the disjunction and DPoP is not added. This library uses private_key_jwt for token endpoint client authentication even when mTLS supplies the sender constraint; the Final Security Profile also permits mTLS there.

The selection guide below also covers public-client deployments supported by the library. Public clients are outside the Final Security Profile's client-scope requirement.

Choosing between them ​

Picking a sender constraint
A decision tree for choosing a sender constraint. Browser-driven clients usually take DPoP, backends that already hold a PKI certificate can take mTLS, B2B and regulated estates follow their scheme or partner requirements, a mixed fleet enables both, and the default when none of these applies is DPoP.Is the client browser-driven?SPA · mobile app · any public clientyesDPoPcnf.jkt — a key the client generates and keepsnoDoes the backend already hold a PKI certificate?you run an internal CA and issue to servicesyesmTLScnf.x5t#S256 — reuse the certificate you issuenoB2B, open banking, or otherwise regulated?the scheme or partner may require client certsyesmTLS, or bothcheck the scheme or partner standardnoA mixed fleet?some SPAs, some backend servicesyesEnable botheach client picks one at registration timenoNot sure? Start with DPoPit needs nothing from your network or your PKI, and a client can move to mTLS later
Both mechanisms end at the same place — a cnf claim the resource server re-checks. The question is only which credential you can realistically get onto every client: a generated key, or a certificate someone has to issue and renew.

The decision usually falls out of the existing infrastructure:

  • SPA, mobile, or any browser-driven client → DPoP. Browsers cannot reliably present client certificates; mobile certificate provisioning is a poor UX. DPoP keys live in memory or in the platform's secure storage.
  • First-party API where you control both ends → DPoP. Lower operational overhead; no PKI required.
  • Backend services with an established internal CA → mTLS. Reuse the existing PKI infrastructure rather than introducing a new key-management surface.
  • B2B service mesh, open banking, regulated environment → mTLS when the applicable scheme or partner standard requires it. RFC 8705 layers token binding on top without changing the wire.
  • Heterogeneous estate (some SPAs, some backends) → Both. Enable both features; the OP advertises them in discovery and each client picks the mechanism it can use.

If you are not sure which one fits, default to DPoP. It carries fewer infrastructure prerequisites and fits more client environments.

Read more ​

  • DPoP (RFC 9449) — proof structure, replay defenses, cnf.jkt, server nonce, public / confidential refresh-binding split.
  • mTLS (RFC 8705) — sub-modes (tls_client_auth vs self_signed_tls_client_auth), cnf.x5t#S256, reverse-proxy wiring.