skip to content

Because a value sent in `Authorization: Bearer` is honoured on possession alone, what design measures reduce the blast radius of one that has been stolen from a production system?

level: seniorimportance: must knowfreq 48%

answer

  1. shrink time / audience / scope / revocability
  2. aud + iss checked, not just signature
  3. forwarding inbound tokens = confused deputy; use token exchange
  4. refresh rotation + reuse detection
  5. sender-constrained: mTLS cnf x5t#S256, DPoP proof header

basics

~20 s

Shrink what a stolen token buys: short lifetime, narrow audience and scope, TLS everywhere, never in URLs or logs. Add revocation you can actually execute, replay detection, and — when it matters — sender-constrained carriage such as mTLS-bound tokens or DPoP.

solid answer

~60 s

Assume theft will happen and reduce the four dimensions of what the attacker gets. - **Time** — minutes-scale access tokens with refresh, so a leaked value expires before most leaks are discovered. - **Audience** — the token names one resource server (`aud`) and is rejected elsewhere, so a token captured by service A cannot be replayed against B. Use token exchange rather than forwarding the inbound token downstream, or a compromised service becomes a confused deputy across the fleet. - **Scope** — least privilege, so a read token cannot write. - **Revocability** — either introspection/short cache TTL, or a deny-list keyed on `jti` that resource servers actually consult. A stateless JWT you cannot revoke means the lifetime *is* your incident response. Add exposure control (TLS, header-only carriage, log scrubbing, no-store), replay detection (same `jti` from two IPs or user agents), refresh-token rotation with reuse detection, and for high-value APIs move to proof-of-possession — mTLS-bound tokens (RFC 8705) or DPoP (RFC 9449) — so the token alone is not enough.

code

http · 4 lines
http
POST /v1/payments HTTP/1.1
Host: api.example.com
Authorization: DPoP eyJhbGciOiJFUzI1NiJ9.token
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7...}.proof-signed-over-htm-htu-iat-jti

go deeper

for a junior

Say that anyone holding the token can use it, so keep it short-lived, only over TLS, out of URLs and out of logs.

for a middle

Add audience and scope restriction, refresh-token rotation, and the fact that a self-contained JWT needs explicit machinery to be revoked.

for a senior

Cover the confused-deputy risk of forwarding tokens, revocation lag as a measured property, replay detection on jti, and when to adopt mTLS-bound tokens or DPoP.

for a principal

Set the fleet policy: default lifetimes and audiences, token exchange between services, a stated revocation-lag SLO, key-rotation drills, and where proof-of-possession is mandatory versus where bearer is an accepted risk.

## The premise A bearer token is honoured because it was presented. The request carries no key, no signature over its contents, and no proof about who is holding it. Everything protecting it is therefore about *exposure* and *consequences*, not about authentication strength. The mature framing is: leaks are a matter of time, so engineer the blast radius. ## Dimension 1 — time Short lifetimes are the highest-leverage control because they need no cooperation from the attacker and no detection. Access tokens measured in minutes (5–15 is a common band) mean that a token found later in a log, a HAR file or a browser profile is already inert. The refresh token becomes the long-lived secret, so it must be treated far more carefully: stored server-side or in an HttpOnly cookie, **rotated on every use**, with **reuse detection** — if an old refresh token is presented again, the whole family is revoked, because either the attacker or the legitimate client is replaying, and you cannot tell which. The cost is more traffic to the authorization server and clock-skew sensitivity, so allow a small `leeway` on `exp` validation and make refresh concurrency-safe (many clients fire refresh from several in-flight requests at once and race). ## Dimension 2 — audience A token should name the service it is for and be rejected everywhere else. Without an audience check, any service that receives a token — including one you do not fully trust, or one that logs headers — can replay it against every other service in the fleet. This is the **confused deputy** problem: service A takes the caller's token and forwards it to B, so a compromise of A grants everything the caller could do anywhere. The fix is token exchange (RFC 8693) or a distinct service credential for the downstream hop, so A calls B as A-acting-for-user with narrowed rights, not as the user with full rights. Resource servers must verify `aud` and `iss` on every request; the number of production systems that verify the signature and forget the audience is remarkable. ## Dimension 3 — scope and privilege Grant the least scope that works, and prefer many narrow tokens over one broad one. A stolen read-only token is an information-disclosure incident; a stolen admin token is a company incident. Sensitive operations can also require a *fresh* authentication (an `auth_time` or `acr` check), so a stolen long-tail token cannot change a password or move money even within its lifetime. ## Dimension 4 — revocability This is the trade-off self-contained tokens make. A signed JWT validated locally is fast and needs no round trip, but nothing consults the issuer, so revocation before `exp` requires extra machinery: an introspection call (accurate, costs a request, usually cached for seconds), or a deny-list of `jti` / subject-and-issued-before entries pushed to resource servers. Whichever you choose, know your **revocation lag** and be able to state it during an incident — "a stolen token stops working within 60 seconds" is a very different posture from "within an hour". ## Exposure control - TLS everywhere, with HSTS for browser clients. - Header carriage only; never a query parameter (URLs are logged, bookmarked, and sent in `Referer`). - `Cache-Control: no-store` on authenticated responses. - Scrub `Authorization` in access logs, HTTP-client debug logs, tracing spans, error reporters and HAR exports. - Bind the credential to its audience in the client, so a default header does not ship your token to third-party hosts. - Rely on the rule that `Authorization` is dropped on cross-origin redirects; never re-attach it manually. ## Detection and response Since prevention is imperfect, instrument for theft. Log a token identifier (`jti`) — never the token — and alert when one identifier appears from two IPs, two ASNs or two user-agent families inside its short lifetime, or when usage patterns jump geographically faster than travel allows. Refresh-token reuse detection is the highest-signal alarm most systems can have. Have a runbook: revoke the token family, invalidate sessions for the subject, rotate any signing key that may have been exposed, and — because key rotation is the one action most teams have never rehearsed — make sure resource servers refresh JWKS on an unknown `kid` so rotation actually propagates. ## When possession alone is not acceptable For high-value APIs, move to **sender-constrained** tokens, where the token is bound to a key the client must prove control of on each request: - **mTLS-bound tokens (RFC 8705)** — the token carries a confirmation claim (`cnf.x5t#S256`) naming the client certificate thumbprint; the resource server checks that the TLS client certificate on this connection matches. Strong, but requires client certificate distribution and a TLS terminator that passes the certificate through. - **DPoP (RFC 9449)** — the client sends `Authorization: DPoP <token>` plus a `DPoP` header carrying a short-lived JWT signed by the client's key and covering the method, URL and a nonce. A stolen token is useless without the private key. Cheaper to deploy than mTLS, but adds clock-skew, nonce and replay-cache concerns. Either one converts "whoever holds the string" into "whoever holds the private key", which is the qualitative jump. The judgement call is whether the operational cost is justified by the value of the API and by who can observe your headers.

  • What exactly does DPoP change about the Authorization header, and what does it buy?
    The scheme becomes `DPoP` instead of `Bearer`, and the request carries an additional `DPoP` header holding a short-lived JWT signed by the client's private key that covers the HTTP method, the target URL, a timestamp and a unique identifier. The access token carries a confirmation claim naming that public key. A stolen access token is then useless without the private key, converting possession-is-proof into proof-of-possession, at the cost of clock-skew handling and a server-side replay cache.
  • If short access-token lifetimes are the main defence, what stops an attacker who also stole the refresh token?
    Refresh-token rotation with reuse detection. Every refresh returns a new refresh token and invalidates the old one, so if the stolen one is used the legitimate client's next refresh fails — and if the old one is presented twice, the server revokes the entire token family and forces re-authentication. This does not prevent the theft but guarantees rapid, automatic detection instead of silent long-term access.
  • Service A receives a user's token and calls service B with the same token. What is wrong with that?
    It makes A a confused deputy: compromising A yields the user's full rights everywhere the token is accepted, and it defeats audience restriction because the token must be valid for many services. Instead A should exchange the inbound token for a downstream token narrowed to B's audience and only the scopes B needs (RFC 8693), or call B with its own service credential plus an explicit on-behalf-of claim.

A bearer token is a hotel key card: you cannot stop one being dropped in the street, so you make it expire at checkout, open only one room, and be cancellable from the front desk.

saying these in an interview costs you the question

  • Treating a signed JWT as inherently safe, without verifying audience and issuer
  • Assuming a stateless JWT can be revoked without any deny-list or introspection mechanism
  • Forwarding an inbound user token unchanged to every downstream service
  • Believing a long access-token lifetime is fine because the token is 'encrypted' (a signed JWT is readable by anyone holding it)
  • Naming DPoP or mTLS as a fix while ignoring their key-management, clock-skew and replay-cache costs

context