Skip to main content
Agent Identity

IETF · September 2023

OAuth 2.0 Demonstrating Proof of Possession (DPoP)

RFC 9449

D. Fett, B. Campbell, J. Bradley, T. Lodderstedt, M. Jones, D. Waite

RFCStableAuthenticationFoundationField guide

OAuth 2.0 Demonstrating Proof of Possession (DPoP) (RFC 9449) is RFC, September 2023. If the agent must use OAuth bearer tokens, DPoP is the most deployable way to make a stolen token from a prompt injection less useful. It is not a substitute for not putting tokens in the model context.

At a glance

Problem
Bearer access tokens are reusable if stolen. DPoP binds an access token to a key the client holds, using a signed proof JWT on each request, without requiring mTLS.
Identity / authn / authz
Sender-constrained authentication of the API call. The token still carries authorization.

Actors

  • Client
  • Authorization server
  • Resource server

How DPoP works

Animated sequence · DPoP vs Bearer

DPoP — Why isn't a stolen access token enough?

Photocopy, or pass glued to a key

A Bearer token is a photocopy: whoever holds the string may use it. DPoP (RFC 9449) glues the pass to a key. The client generates a key pair, then sends a DPoP proof JWT on the token request AND on every resource request (htm/htu). The access token's cnf.jkt must match. Stolen paper without the private key is a blank.

Authorization serversends
Client with keygets
API
Thief

Now · 1/12· Discover· RFC 6750You are here

Authorization server→Client with key·Mints a Bearer access token — just a string

The AS issues token_type=Bearer. Anyone who copies the string can present Authorization: Bearer.

Why Contrast hop. Bearer means possession of the string is authorization. The authorization-code grant that produced this token is the OAuth 2.1 sequence — not replayed here.

A pass that is a photocopy — whoever holds the paper gets in.

  • token_type=Bearer from the spec
  • Authorization from the spec

Solid = from the spec

Step 1 of 12: as to agent. Mints a Bearer access token — just a string RFC 6750
Speed
ShareLinkedInX

RFC 9449. DPoP is application-layer proof and works through TLS proxies. AAuth uses HTTP Message Signatures instead — do not stack proofs naively. Proofs are required on the token request and on each resource request.

In one board · Bearer vs DPoP

Photocopy, or pass glued to a key

Bearer

Photocopy

Authorization: BearerAuthorization: Bearer

Whoever holds the string may use it until it expires. Prompt injection, logs, and traces are copy machines.

  • Thief

    same pass, no key needed →

    API

DPoP

Glued to a key

access token + cnf.jkt

Each request carries a short-lived proof JWT covering method and URL. Stolen paper without the private key is a blank.

  • Client with key

    pass + fresh proof →

    API

RFC 9449. DPoP is application-layer proof — it works through TLS proxies. AAuth uses HTTP Message Signatures instead; do not stack proofs naively. mTLS is the other glue.

Hop list

Sequence above; this list is the same hops for reading and search. Each row is one message.

  1. 1

    Authorization server→Client with keyRFC 6750Mints a Bearer access token — just a string

  2. 2

    Client with key→APIRFC 6750Authorization: Bearer — the API trusts the string

  3. 3

    Client with key→ThiefRFC 6750Logs, traces, or prompt injection photocopy the string

  4. 4

    Thief→APIRFC 6750Same pass, no key needed — the API cannot tell

  5. 5

    Client with key→Client with keyRFC 9449Generates a DPoP key pair and keeps the private key

  6. 6

    Client with key→Authorization serverRFC 9449Token request with a DPoP proof JWT (htm=POST, htu=token endpoint)

  7. 7

    Authorization server→Authorization serverRFC 9449AS checks the DPoP proof: signature, htm, htu, iat

  8. 8

    Authorization server→Client with keyRFC 9449Issues an access token with cnf.jkt (thumbprint of the client's DPoP key)

  9. 9

    Client with key→APIRFC 9449Resource request: Authorization: DPoP plus a new DPoP proof JWT (htm/htu of this call)

  10. 10

    API→APIRFC 9449RS checks cnf.jkt against the proof's JWK and verifies htm/htu

  11. 11

    API→Client with keyRFC 9449Responds — the caller proved the key the token is glued to

  12. 12

    Thief→APIRFC 9449Photocopy without the private key — 401, the paper is a blank

01

The gap for agents

Bearer access tokens are reusable if stolen from an agent prompt or log. DPoP (RFC 9449, September 2023) binds an access token to a client key using a signed proof JWT on each request, without requiring mTLS — the most deployable sender-constraint for agents that are not in a mesh.

It is not a substitute for keeping tokens out of model context. A stolen token plus a stolen key is still game over. It does stop 'I found a token in a gist' replay from a different machine.

02

Actors and trust boundaries

The client's DPoP key is the sender. The AS puts the JWK thumbprint in the access token cnf.jkt. The RS requires a fresh proof signed by that key covering htm and htu. TLS-terminating proxies do not break DPoP the way they break mTLS passthrough.

03

Mechanics

Client generates a DPoP key pair. Token endpoint (and later RS) receive a DPoP header: a JWT with typ dpop+jwt, jwk in the header, claims htm, htu, iat, jti, and when presenting an access token, ath (hash of the token). AS issues token_type=DPoP with cnf.jkt. RS checks proof freshness, uniqueness of jti (replay cache), and binding.

Nonce mechanism lets the RS/AS demand a fresh proof. Authorization codes can be bound to the DPoP key (FAPI 2.0 requires this when using DPoP).

DPoP proof JWT (htm, htu, iat, jti, ath)from spec
Quoted RFC 9449: per-request, short-lived, method and URI binding. ath when sending an access token.
cnf.jktfrom spec
Quoted: JWK thumbprint binding in the access token.
Authorization: DPoP / DPoP: <jwt>from spec
Quoted presentation: token_type DPoP, proof in the DPoP header — not Bearer.

04

Step-by-step flows

Flow 1

Bind then prove

  1. 1

    Client creates a DPoP key (often ephemeral per session).

  2. 2

    Token request includes a DPoP proof. AS returns an access token with cnf.jkt.

  3. 3

    Each RS call: new proof covering this method and URI, plus the access token, Authorization scheme DPoP.

  4. 4

    RS verifies token signature/introspection, jkt matches the proof key, proof signature, htm/htu, iat window, jti not replayed.

05

Identity vs authentication vs authorization

Sender-constrained authentication of the API call. The token still carries authorization. DPoP does not identify the user; OIDC still does that at the AS.

06

How it composes

07

What bites agent implementers

  • Proof replay

    Without a jti cache and tight iat, a captured proof+token pair replays at the same URI. Use nonce where possible.

  • htu mismatches

    Proxies that rewrite URLs break htu. Normalize carefully; do not skip the check.

  • Key in the agent filesystem

    If the DPoP private key sits next to the token in the same dump, you have not gained much. Isolate keys.

08

Stability — what you can ship

RFC 9449, September 2023. Stable. Ship it for any agent that must use OAuth tokens outside a mesh. https://www.rfc-editor.org/rfc/rfc9449.html

Catalog claims (short form)

DPoP proof JWT
Per-request, short-lived, includes htm/htu.
cnf.jkt
JWK thumbprint binding in the access token.

Implementer notes

DPoP is application-layer PoP and works through TLS-terminating proxies. AAuth instead uses HTTP Message Signatures (RFC 9421) as its PoP. Do not mix proofs naively.

Related

Primary sources

  • RFC 9449https://www.rfc-editor.org/rfc/rfc9449.html