Token Exchange

a client posts a token it already holds to the token endpoint and gets a different token back, so it can act for the user towards another service, or let one service act on behalf of another.

Where it stands
Published RFC
Official text
RFC 8693
Documented support
3
Activity
3 signals in 90 days

Read the RFC side by sideExplainer source on GitHub

When you reach for it

  • A resource server that must call a backend API with the caller's identity: instead of forwarding the access token it received, it exchanges that token for a short-lived one scoped to the backend (Section 2.3).
  • A service acting for a user where the backend must see both parties: delegation keeps the user as sub and records the service in an act claim, so the backend can tell who is calling for whom (Section 1.1, Appendix A.2).
  • A service that has to look exactly like the user: impersonation issues a token that names only the user, so the holder is indistinguishable from the subject within that token's scope (Section 1.1, Appendix A.1).
  • A client that needs a different kind of token than the one it holds: trade a SAML assertion or ID token for a JWT, for example one to use as an authorization grant at another authorization server, with token_type set to N_A when the result is not an access token (Section 2.2.1, Section 3).
Diagram
flowchart LR
    A["Service A holds an access token issued for A"] -->|"forwards it to B"| B["B receives a token not meant for it, no record of who is acting"]
    A -->|"exchanges it at the authorization server"| C["Short-lived token for B, with sub and optional act claim"]

The problem it solves

A service receives a request carrying an access token whose audience is that service. To call a second service it needs a token the second service accepts. Passing the original token along gives the second service a credential that was never issued for it, and it loses any record of which service is acting. RFC 8693 makes the authorization server a Security Token Service: a client presents a subject_token (and optionally an actor_token) and receives a new token for the intended resource or audience, with the delegation chain written into the token itself (Section 1, Section 4.1).

How it works

Diagram
sequenceDiagram
    participant C as Client (service A)
    participant AS as Authorization Server (STS)
    participant RS as Backend B
    C->>AS: POST /token grant_type=token-exchange, subject_token, subject_token_type, resource or audience, scope
    Note over C,AS: optional actor_token and actor_token_type for delegation
    AS->>AS: validate subject_token and actor_token, check may_act, apply policy
    AS-->>C: access_token, issued_token_type, token_type, expires_in, scope
    C->>RS: request with the issued token
    RS-->>C: response

The client sends grant_type=urn:ietf:params:oauth:grant-type:token-exchange with the token it holds in subject_token and its type in subject_token_type, naming the target with resource (a URI) or audience (a logical name). For delegation it adds an actor_token. The authorization server validates every token in the request, decides what it will issue, and returns it in access_token together with issued_token_type, which says what kind of token came back. If the result is a delegation token, its sub is the original subject and act identifies the current actor, with earlier actors nested inside (Section 2.1, Section 2.2.1, Section 4.1).

What the spec requires

  • grant_type MUST be urn:ietf:params:oauth:grant-type:token-exchange; subject_token and subject_token_type are REQUIRED; actor_token_type is REQUIRED when actor_token is present and MUST NOT be included otherwise (Section 2.1).
  • The authorization server MUST validate the subject_token, and the actor_token if present, according to their types (Section 2.1).
  • resource is an absolute URI that MUST NOT include a fragment; audience is a logical name unique within the authorization server; both may repeat and may be combined (Section 2.1).
  • The response MUST include access_token, issued_token_type and token_type; token_type is N_A when the issued token is not an access token; scope is REQUIRED when it differs from what was requested (Section 2.2.1).
  • invalid_request MUST be returned for invalid requests or rejected tokens; invalid_target SHOULD be returned when the server cannot issue a token for a requested resource or audience (Section 2.2.2).
  • Consumers of a token with an act claim MUST consider only the top-level claims and the current actor when making access-control decisions; prior actors are informational (Section 4.1).
  • A may_act claim in a token states which party is authorized to become the actor for that token's subject (Section 4.4).

Watch out

  • Client authentication is left to the authorization server. Without it, anyone holding a token can exchange it, so authentication is what lets the server decide which entities may impersonate or receive delegations (Section 2.1).
  • An exchange is a one-time event. Renewing the input token does not extend the output, and revocation of the input is not propagated unless the deployment adds that (Section 2.1).
  • Requesting many targets at once makes rejection more likely; the effective access is the product of the requested scopes across all targets, so keep requests narrow (Section 2.1.1).
  • Delegation and impersonation let one principal exercise another's rights. Use the scope claim to restrict where delegated rights apply and keep token lifetimes short (Section 5).
  • Tokens may carry sensitive data and MUST be sent only over encrypted channels; if the content must be hidden from the client, encrypt the token to its intended recipient (Section 6).

Sources

How it relates to other specs

Relations diagram
flowchart LR
  s_token_exchange["Token Exchange"]
  s_oauth2["OAuth 2.0"]
  s_identity_chaining["Identity and Authorization Chaining"]
  s_id_jag["Identity Assertion JWT Authorization Grant"]
  s_txn_tokens["Transaction Tokens"]
  style s_token_exchange stroke-width:3px
  s_token_exchange -->|Extends| s_oauth2
  s_identity_chaining -->|Builds on| s_token_exchange
  s_id_jag -->|Builds on| s_token_exchange
  s_txn_tokens -->|Builds on| s_token_exchange

Documented support

Adoption lists only support stated in official documentation (the vendor's or project's own, or a standards body's page naming it), and a weekly check confirms the cited phrase is still on that page. Experimental, alpha and beta support is counted separately as preview.

Products, identity providers and services

  • Keycloak (Identity provider): Standard token exchange implementing RFC 8693 source (verified 2026-11-11)
  • Google Cloud (Cloud): Security Token Service implements the standard OAuth 2.0 token exchange source (verified 2026-11-11)
  • Okta (Identity provider): On-Behalf-Of Token Exchange on custom authorization servers source (verified 2026-11-11)

Activity

Revisions from the Datatracker, explanations from this site's daily runs, and mailing-list subjects naming the draft.

Explanations on this site

  • DiscussionIETF WIMSE

    The evidence-binding authors will narrow a claim about suspended prior actors so it no longer conflicts with RFC 8693 Section 4.1.

    New I-D: draft-thaha-wimse-agent-evidence-binding-00 (evidence binding for AIMS audit)

    Read the explanation

    Shameer accepts Mikhail's two corrections. The statement that a Serving Party cannot refuse a call because a prior actor is suspended was too broad, because RFC 8693 Section 4.1 only rules out using nested act claims for that decision, not separately verified delegation tokens. The wording becomes: cannot refuse a call on the basis of the nested act claims because a prior actor has since been suspended. The phrase about history being neither complete nor evidence is replaced with: does not by itself establish that it is complete, or that any hop held or narrowed authority. Both edits will go into the next revisions of the SCITT draft and the Evidence Binding draft. No draft text was posted in this message.

    • A Serving Party deciding whether it may deny a call based on a suspended prior actor, using verified delegation tokens rather than nested act claims.
    • Related in OAuth WG: draft-ietf-oauth-transaction-tokens. Weak link only: both deal with carrying delegation context across hops, but the material does not reference it.
  • DiscussionIETF WIMSE

    Mikhail asks the evidence-binding authors to reword claims that a serving party cannot refuse a call over a suspended prior actor.

    New I-D: draft-thaha-wimse-agent-evidence-binding-00 (evidence binding for AIMS audit)

    Read the explanation

    Kieran has accepted that RFC 8693 Section 4.1 keeps prior actors in nested act claims out of access-control decisions, and the authors will take that reading in -02. Mikhail notes the SCITT -02 text still says a Serving Party cannot refuse a call because a prior actor was suspended. He points out that separately verified delegation tokens give another basis: a chain in the draft-asor-wimse-agent-delegation-chain-03 format is verified hop by hop, and step 7 denies when an accepted status answer marks any token revoked, within the status list freshness window. He suggests wording limited to refusal on the basis of nested act claims, and rewording the phrase that history is neither complete nor evidence.

    flowchart LR
    A[Token with nested act claims] --> B[Serving party reads top-level claims and current actor only]
    C[Delegation chain tokens] --> D[Verify each hop]
    D --> E[Check status list answer]
    E -->|any token revoked| F[Deny]
    • Designing an audit or serving-party policy for multi-hop agent calls where an upstream actor is later suspended.
    • Choosing whether per-hop authority is checked from act claims (attribution only) or from separately verified delegation tokens with status references.
    • Related in OAuth WG: draft-ietf-oauth-status-list. The delegation-chain draft's status references and freshness window are checked against a status list answer.
  • New proposalOAuth WG

    A new individual draft defines Agent Passports and Delegation Receipts, issued through Token Exchange.

    New draft: Agent Passports and Delegation Receipts (draft-le-agent-passport-00)

    Read the explanation

    draft-le-agent-passport-00 defines two signed objects. An Agent Passport is a JWT that binds an agent's key (cnf) to the accountable organisation and to a digest of its build. Delegation Receipts form a chain from a user through agents and sub-agents. Each step is key-bound and can only narrow the one before, and the relying party verifies the chain and intersects it with the user's own access. Root receipts are obtained with RFC 8693 Token Exchange, using the passport as actor token and a new requested_token_type urn:ietf:params:oauth:token-type:agent-delegation. Status uses the Token Status List draft. The open question is whether grants should be an RFC 9396 authorization_details type. The author mentions a side meeting at IETF 127 and an IPR disclosure, and says the draft complements draft-klrc-aiagent-auth and AAuth.

    sequenceDiagram
      participant U as User
      participant A as Agent
      participant AS as Authorization Server
      participant RP as Relying Party
      A->>AS: Token Exchange, actor_token = Agent Passport
      AS-->>A: root Delegation Receipt (agent-delegation type)
      A->>A: sub-agent receipt, narrower than parent
      A->>RP: passport and receipt chain
      RP->>RP: verify chain, intersect with user access
    • An authorisation server that already supports RAR and wants to issue agent delegation grants as authorization_details
    • A relying party that must check, across organisations, that a sub-agent's authority is a subset of the user's
    • Related in OpenID: openid/aauth. The author says the draft is meant to complement AAuth.