Free & open · no paywall, ever

IntegrAuth Academy

Identity, security and practical AI — free, from zero. Byte-sized lessons, real diagrams, hands-on labs, zero fluff. Two programs, each with its own exam and certificate.

20 tracks 241 lessons 242 hands-on labs 230+ diagrams 100% free

English is hard? Your browser can translate this page into Tamil, Telugu, Kannada, Malayalam or Hindi. Tech words stay in English so you learn the real names.

How?
  • Chrome on phone: ⋮ menu → Translate
  • Chrome on computer: right-click → Translate to …
  • Edge: the translate icon in the address bar
Identity & API Security Track 3 · Token Security
0/166 lessons read
TOKEN SECURITY · LESSON 5

Stolen-token defenses II — DPoP, mTLS & encrypted tokens

The last lesson made a stolen token cancelable. This lesson makes the copy useless. Your goal: explain how DPoP, mTLS and encryption each help, and why they are different.

Key points
  • A bearer token works for anyone who holds it.
  • A sender-constrained token needs a secret key that the thief lacks.
  • DPoP fits browser and phone apps. mTLS fits services.
  • Encryption hides a token. It does not stop replay.

From bearer to sender-constrained

By default, an access token is a bearer token. Like cash, whoever holds it can spend it. A sender-constrained token is tied to a secret that only the real owner has. The thief can take the token all day. Without the secret, it is a dead string.

Think of it like…

Cash versus a debit card. Whoever holds cash can spend it. A photo of your card number is not enough, because every payment still needs your PIN. Sender-constraining puts a PIN on your token.

DPoP: tie the token to a browser key

The browser makes a non-extractable key. It is a private key that the browser will sign with but never hand over. So bad JavaScript cannot copy it. The token carries the key's fingerprint in the claim cnf.jkt. On every call, the browser signs a small fresh proof. The API then checks four things:

  • The proof's key matches cnf.jkt.
  • The proof is for this exact method and URL, and it names a hash of this token.
  • The proof's proof ID is new, so an old proof cannot be played again.
  • The proof is recent.

A copy without the key gets 401 invalid_token. A broken or reused proof gets invalid_dpop_proof. This method is called DPoP, for Demonstrating Proof-of-Possession.

Browser (key)AttackerResource API1 · bind token → cnf.jkt2 · call + fresh signed proof4 · replay stolen token, no key3 · ✓ 200 accepted✗ 401 invalid_token — copy uselessgenerate non-extractable keyproof key == cnf.jkt? jti fresh?
Figure — A PIN on every call. The real browser signs a fresh proof. The attacker's copy has no key and is refused. DPoP is the browser-friendly answer to token theft.

Four ways to disarm a copy

🔑 DPoP

Tied to a key held by the client. Best for browser and phone apps that have no certificate.

📜 mTLS

Tied to a TLS client certificate (cnf.x5t#S256). The API reads the checked certificate from the TLS layer. Great between services. See TLS and mutual TLS in practice.

🔒 Encrypted token (JWE)

A signed token inside an encrypted envelope. The holder cannot read it. Only the API can open it.

🤖 Agent tokens

Tie an AI agent's on-behalf-of token to the agent's own key. A copy that leaks through logs is then useless.

Encryption is not sender-constraining

A normal token is signed, not secret. Anyone can base64-decode it and read the email, scopes and roles. A JWE (an encrypted token) stops a thief from reading it. DPoP and mTLS stop a thief from using it. These are different problems, so a JWE can still be replayed. Use both.

Agents leak tokens, so tie theirs too

When an AI agent acts for you, a control layer gives it an on-behalf-of token through token exchange. The token holds the agent's identity, your authority and a narrow scope. Agent tokens leak easily. They pass through logs, traces and tool output. A prompt injection can try to steal one. So tie the token to the agent's own non-extractable key (cnf.jkt). A stolen copy then gets 401 invalid_token.

AI agent (key)AttackerGoverned API1 · exchange OBO token → cnf.jkt2 · call + proof (on-behalf-of Maya, scope: read)4 · replay leaked token, no key3 · ✓ 200 accepted✗ 401 invalid_token — copy inertproof key == cnf.jkt? jti fresh?
Figure — Tie the agent's credential. Control the action and bind the token. A stolen OBO token then buys the attacker nothing. See delegated access.

On a hard failure, a real client throws the token away and signs in again. It never keeps the token and retries.

Why it matters

Real systems layer all of this. A leaked token is short-lived, cancelable, hard to use and unreadable. No single defense is the whole answer.

Go deeper: standards and extras

DPoP is RFC 9449. mTLS tokens are RFC 8705. Token exchange is RFC 8693. A script on the page can still ask the browser to sign while the page is open. The API can remember recent proof IDs to stop reuse.

An API can also demand a server-chosen number, a nonce, inside the proof. This limits how long a stolen proof stays useful. The API signals this with the error use_dpop_nonce.

DPoP can also tie the refresh token of a public client to the key. Then a stolen refresh token is useless too. With mTLS, the proof is the TLS handshake itself, so there is no extra header.

🧪 Interactive lab — enable JavaScript to play with this one.

🧪 Try it free

Look at a DPoP proof and how it binds with DPoP Check.