Bearer tokens are popular for a reason. They are simple to issue, simple to send, and simple for an API to validate. The client presents the token, the resource server checks whether it is valid for the request, and the API can move on.
That simplicity is also the problem.
RFC 6750 defines a bearer token as a token where any party in possession can use it without proving possession of cryptographic key material. That is not a defect in the specification. It is the security model. If the token stays confidential, the model works. If the token leaks, the token itself is enough.
This is why bearer-token security is less about dramatic attack stories and more about ordinary engineering hygiene. Logs, traces, crash dumps, browser tooling, reverse proxies, support workflows, and copied command lines can all become token transport.
Bearer means possession is enough
When an API receives a bearer access token, it can validate several important things:
- Was the token issued by a trusted authorization server?
- Is the token still within its lifetime?
- Is the token meant for this audience?
- Does the token carry the right scope or authorization data?
- Has the token been revoked, if the API checks revocation or introspection?
Those checks matter. They reduce blast radius and keep APIs from accepting tokens that were issued for another resource or another purpose.
But they do not answer one question: is the caller holding the token the same client the token was issued to?
With a plain bearer token, the resource server usually cannot tell. A legitimate client and an attacker who copied the token can send the same Authorization: Bearer ... header. If all other validation succeeds, the copied token can look like a valid request.
Leakage rarely looks exotic
Teams often talk about token theft as if it starts with a full system compromise. Sometimes it does. More often, the first failure is smaller:
- an access token appears in an application log because request headers were captured too broadly
- a reverse proxy stores headers in diagnostic output during an incident
- a developer copies a curl command with a production token into a ticket or chat thread
- browser storage exposes a token to injected JavaScript or an over-broad extension
- telemetry captures headers, query strings, or failed requests before redaction rules are correct
- a crash report includes runtime state that nobody expected to contain credentials
The practical issue is not only that the token leaked. It is that the leaked token is portable. If an attacker can copy it from one place and present it somewhere else before it expires, replay is possible.
RFC 9700, the OAuth 2.0 Security Best Current Practice, recommends mechanisms such as sender-constrained access tokens to prevent misuse of stolen or leaked access tokens, and it also recommends restricting token privileges and audiences. Short lifetimes and narrow scopes help too. They reduce the time window and privilege of a stolen token. They do not change the bearer property itself.
Replay is the failure mode
The replay story is uncomfortable because nothing strange has to happen at the API boundary. The attacker does not need to break the signature on a JWT. They do not need to understand the user’s password. They only need to send a valid token while it is still accepted.
sequenceDiagram
autonumber
participant Client as Legitimate client
participant API as Resource server
participant Logs as Logs and tooling
participant Attacker as Attacker
Client->>API: Send bearer token
API->>API: Validate issuer, expiry, audience, scope
API-->>Client: Request accepted
API-->>Logs: Token appears in diagnostic data
Attacker->>Logs: Copy token
Attacker->>API: Replay copied token
API-->>Attacker: Accepted while token is valid
Client->>API: Send bound token with proof
API->>API: Validate token and proof
API-->>Client: Request accepted
Attacker->>API: Replay token without proof
API-->>Attacker: Request rejected
The diagram is deliberately ordinary. The token does not need to travel through a malicious-looking system. It only needs to appear somewhere a different party can read it.
Sender constraint changes the last step
Sender-constrained tokens add a second condition to resource access. The caller must present the token and prove possession of material to which the token is bound.
In standards terms, two important OAuth mechanisms are DPoP and OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens. This post discusses them as standards, not as a ProAuth feature announcement.
DPoP works at the application layer. The client creates a key pair and sends a signed DPoP proof JWT with the request. RFC 9449 defines proof data such as htm for the HTTP method, htu for the target URI, iat for the proof timestamp, and jti for a unique proof identifier. For protected resource access, the proof also includes ath, a hash of the access token. A DPoP-bound access token can carry confirmation data such as cnf.jkt, the thumbprint of the public key.
The important part is the relationship: the resource server checks the access token, checks the signed proof, and checks that the key used for the proof matches the key binding in the token. Copying only the token is no longer enough.
mTLS certificate-bound access tokens use a different proof material. RFC 8705 defines certificate-bound access tokens where the token can carry confirmation data such as cnf.x5t#S256, a SHA-256 thumbprint of the client certificate. The protected resource compares that value with the certificate used on the mutually authenticated TLS connection. If the token and certificate do not match, the request is rejected.
Both approaches reduce bearer-style replay. They do not make tokens unimportant. They make stolen tokens less useful when the attacker does not also have the matching private key or certificate.
Sender constraint is not a shortcut around architecture
Sender-constrained tokens are a strong control, but they are not magic glue for every token-handling problem.
If malicious JavaScript can run inside a browser client and use the sender key while the application is active, DPoP does not turn that browser into a trusted environment. If certificate material is copied with the token, mTLS binding does not help. If an API accepts a proof-bound token as a plain bearer token because resource servers are upgraded inconsistently, the intended control can be lost at the exact place it matters.
The architecture still has to reduce exposure:
- keep access tokens out of browser-accessible storage when a server-side boundary is feasible
- avoid putting tokens in URLs, because URLs are copied, logged, cached, and forwarded too easily
- redact authorization headers and token-like values from logs and telemetry
- restrict access tokens to the audience and privileges they actually need
- keep access-token lifetimes short enough that leakage has a bounded window
- treat replay signals, such as reused proofs or unexpected token reuse, as security events
Sender constraint should be layered onto that foundation. It reduces replay value; it does not excuse sloppy token handling.
When to consider sender-constrained access tokens
The decision usually becomes easier when you look at where the token travels.
Consider sender constraint when access tokens cross boundaries where copying is plausible and the protected resource is valuable enough to justify the operational cost. That often includes high-value APIs, regulated workloads, machine-to-machine integrations, partner APIs, financial or health data access, and internal service chains where one broad token could otherwise be replayed across systems.
DPoP is often discussed where application-layer proof is more practical than TLS client certificates, including public clients and environments where certificate operations would be difficult. mTLS is often a better fit for controlled server-to-server environments where certificate issuance, rotation, trust stores, and proxy behavior can be operated consistently.
That is a design tradeoff, not a ranking. DPoP brings key management and proof replay checks into the application layer. mTLS brings certificate lifecycle, endpoint topology, and proxy trust boundaries into the operational model. In both cases, the resource server has real work to do. It must validate the token and the proof, and it must fail closed when the proof is missing or wrong.
The review question
For an existing API estate, the useful question is not “Do we use bearer tokens?” Most OAuth deployments do.
The better question is:
If this access token appeared in a log, trace, browser, ticket, or developer workstation, where else could it be replayed before it expires?
That question turns token security into an architecture review instead of a vocabulary debate. It forces teams to inspect token storage, logging, audience restriction, scopes, lifetimes, browser boundaries, API validation, and sender-constraint options together.
Bearer tokens are not obsolete. They are still part of OAuth. But the security baseline has moved away from treating possession as the only interesting fact. Modern OAuth deployments need to ask who can present the token, where the token is valid, what proof travels with it, and what happens when someone tries to replay it somewhere else.
Further reading: