Sender-Constrained Tokens
The single most important concept for this project, because it’s the direct heir to the mobile bank’s per-request hash. FAPI 2.0 mandates it; see the rulebook.
Bearer vs proof-of-possession
A bearer token is like cash or a concert wristband — whoever holds it spends it. Steal it (from a log, an XSS payload, a leaky proxy) and you have full access. This is the biggest web-specific risk, because the browser leaks tokens in ways a native app doesn’t.
A sender-constrained token (proof-of-possession, PoP) is like a named ticket you must show ID for. The token is bound to a key the legitimate client holds, and every use requires proving possession. A stolen token alone is worthless.
This is the exact web analog of the mobile per-request hash. On mobile the enclave key signs each request; on web a client-held key proves possession on each call. Same principle, different key store.
See it happen — steal a bearer token, then steal a DPoP one:
The lesson: a bearer token is like cash — whoever holds it spends it, so a stolen one grants full access. A sender-constrained token (DPoP) is bound to a private key the client proves it holds on every call. Steal the token and you still fail, because the key never left the legitimate client. This is the web analog of the mobile per-request hash.
DPoP — Demonstrating Proof of Possession (RFC 9449)
Application-layer:
- The client generates its own key pair — in the browser via WebCrypto, ideally non-extractable in IndexedDB.
- On every request it builds a small DPoP proof JWT, signed with the private key, carrying the HTTP method (
htm), URL (htu), timestamp (iat), a unique id (jti) for replay protection, and a hash of the token (ath). It rides in theDPoPheader. - At issuance the AS binds the token to the thumbprint of the client’s public key — the
cnf.jktclaim. - The gateway, on each call, checks: proof signature valid,
htm/htumatch, the proof’s key thumbprint equals the token’scnf.jkt,jti/iatfresh.
Steal the token and, without the private key that never leaves the client, you can’t forge a matching proof.
Analogy: you hand-sign a fresh note for each request proving you still hold the key.
mTLS-bound tokens (RFC 8705)
Transport-layer. The client holds an X.509 certificate, opens a mutually-authenticated TLS connection, and the AS binds the token to the certificate thumbprint (cnf.x5t#S256). Every call presents the same cert; the gateway checks it matches. The cert doubles as strong client auth.
Analogy: the whole phone line is locked to your certificate; the token only works on that line.
The comparison that drives the decision
| Dimension | DPoP | mTLS |
|---|---|---|
| Layer | Application (HTTP + JWT) | Transport (TLS) |
| Key store | Browser WebCrypto | Cert store / HSM |
| Provisioning to millions of browsers | Trivial — key made in-browser | Impractical — certs don’t distribute to consumers |
| Behind a TLS-terminating CDN | Works — binding is app-layer | Fragile — cert must be forwarded carefully |
| Infra needed | None beyond code | Full PKI: issuance, rotation, revocation |
| Best fit | Consumer web / SPA | B2B, server-to-server, Open Banking TPPs |
Recommendation for a consumer web bank: use DPoP for the browser channel and keep mTLS for server-side and B2B/TPP paths. FAPI 2.0 accepts either, so this is architecture, not compliance.
The decision underneath: where do tokens live?
A browser SPA is a public client — it can’t safely hold a secret or a cert. Two patterns:
- BFF (Backend-for-Frontend). A server-side confidential client runs the OAuth flow and holds the tokens; the browser holds only an HttpOnly, SameSite cookie. Tokens never touch the browser — the whole token-theft-from-browser risk disappears. Usually the stronger posture for a bank, and it resolves the mTLS provisioning problem too.
- SPA + DPoP. Tokens live in the browser, DPoP-bound to a non-extractable key. Simpler topology, but you now trust the browser to hold keys safely.
The whole flow assembled
This single spine connects C1 (the passkey at step 3), C2 (the DPoP binding at 5–7) and C5 (gateway validation at 7).
Client crib-sheet
- Bearer = cash. Whoever holds it spends it.
- Sender-constrained = named ticket + ID. Useless if stolen.
- DPoP = sign a fresh note per request proving you hold the key. (the web version of the mobile hash)
- mTLS = the whole line is locked to your certificate.