Use case · RFC 8693
Token Exchange for Delegation Across Service Boundaries
A user calls service A; service A must call service B on the user's behalf. Forwarding the original token is the wrong answer. This is the right one.
When do you need token exchange instead of forwarding the access token?
The moment a request crosses more than one service boundary carrying user identity. A frontend calls an orders API; the orders API needs data from a payments API. The payments API must know which user the request is for and which service is asking — and it must not accept a token that was minted for someone else's audience.
Token exchange (RFC 8693) lets service A trade the inbound user token for a new token: same subject, new audience, narrower scope, with the delegation chain recorded in the act claim.
Who are the actors and where are the trust boundaries?
- Subject — the end user, authenticated upstream. Never talks to the token endpoint in this flow.
- Actor — service A, a confidential client with its own credentials. The party performing the exchange.
- Authorization server — PingAM. The only component allowed to mint tokens; every exchange is a policy decision, not a formality.
- Downstream resource — service B. Validates audience, scope, and — if it cares about the chain — the
actclaim.
The trust boundary sits between A and B. The design goal: B never has to trust A's claims, only the authorization server's signature.
How does the flow work, step by step?
- User → Service ARequest arrives with the user's access token (audience:
service-a). - Service A → PingAMA calls
/access_tokenwithgrant_type=urn:ietf:params:oauth:grant-type:token-exchange, passing the inbound token assubject_tokenand authenticating itself with its own client credentials. - PingAMValidates the subject token, evaluates the
may_actpolicy — is service A allowed to act for this subject toward this audience? — and mints a new token: subject unchanged,aud=service-b, scopes downscoped,act.sub=service-a. - Service A → Service BA calls B with the exchanged token. Its own original token never leaves its boundary.
- Service BValidates signature, audience, scope. Audit logs record both the subject and the acting service — the full chain, per hop.
POST /am/oauth2/realms/root/access_token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {service-a credentials}
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={inbound access token}
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=service-b
&scope=payments:read
Which token combinations does the exchange support?
RFC 8693 is not limited to trading one access token for another. Both the subject_token and the requested_token_type can be an access token or an ID token, and adding an actor_token switches the semantics from impersonation to delegation. PingAM supports the full matrix:
| Subject token | Requested type | Typical use |
|---|---|---|
| Access token | Access token | Re-audience or downscope a token for a downstream API when the chain does not need to be visible. |
| ID token | Access token | A service holds proof of authentication (OIDC login) and needs an API credential without re-running an authorization flow. |
| Access token | ID token | A downstream component needs identity claims (who is this?) rather than API access — e.g. personalization or audit enrichment. |
| ID token | ID token | Re-mint identity claims for a different audience or with a different claim set across trust domains. |
| Subject token | Actor token | Requested type | Typical use |
|---|---|---|---|
| Access token (user) | Access token (service) | Access token | The canonical case in this article: service A calls service B on the user's behalf, auditable per hop. |
| Delegated access token | Access token (service) | ID token | Chained delegation: a token that already carries an act claim is exchanged again — PingAM nests the previous actor inside act.act, preserving the full chain A→B→C. |
| ID token (user) | ID token (service) | Access token | Both parties prove identity via OIDC; the exchange mints the API credential with the delegation relationship embedded from the start. |
Two details that decide which row you need. First, requested_token_type answers a different question than the grant: access token = "may I call this API?", ID token = "who is this, verifiably?". Requesting an ID token to authorize API calls is a category error — resource servers should not accept ID tokens as credentials. Second, in delegation the actor_token is how the acting service proves its own identity in the exchange; the may_act policy on the subject token is what authorizes that specific actor. Both must hold, or PingAM rejects the exchange.
What goes wrong in real deployments?
Token replay disguised as pragmatism
Teams forward the original token "temporarily" because it works. It grants every downstream hop the full original scope, destroys audience restriction, and makes per-hop revocation impossible. It never gets removed until an audit forces it.
Exchange without downscoping
An exchange that returns the same scopes to a new audience is replay with extra steps. The value is in the narrowing: request only what the downstream call needs.
Impersonation where delegation was intended
If the issued token drops the act claim, service B sees the user with no trace of the intermediary. Fine for some legacy integrations; fatal for anything with audit requirements. Decide explicitly, per audience — never by default.
Chains without depth limits
A→B→C→D, each hop exchanging again. Without a policy limit on chain depth, latency and blast radius grow silently. Most architectures need at most two hops of delegation; more is a design smell.
How do you implement this on PingAM?
PingAM supports RFC 8693 natively on the standard token endpoint from AM 7.0. The pieces that matter in practice:
- Enable the grant type per OAuth2 client —
urn:ietf:params:oauth:grant-type:token-exchangemust be in the client's allowed grants; it is off by default. may_actpolicy — controls which clients may act for which subjects. Model it as a real authorization decision in the OAuth2 Provider service, not as a wildcard.- Stateless (client-based) tokens — for high-throughput service meshes, exchanged tokens as signed JWTs avoid a CTS lookup per hop. The trade-off is revocation latency; pair with short TTLs (60–300 s) on exchanged tokens.
- Scope implementation plugin — the default scope validator will happily grant whatever the client asks; a custom scope validation script is where downscoping policy actually lives.
- PingGateway at the edge — a common MYTO pattern: gateway performs the exchange on behalf of legacy services that cannot, keeping RFC 8693 adoption incremental.
When should you not use token exchange?
- Single-hop architectures — if A never calls B with user context, audience-restricted tokens from the original grant are sufficient.
- Pure machine-to-machine — no user subject involved: use client credentials, not an exchange with a synthetic subject.
- Same trust domain, same audience — if A and B are genuinely one security boundary (rare, but real in some monolith-decomposition phases), the exchange adds latency without adding isolation.
Frequently asked questions
- What is the difference between delegation and impersonation in token exchange?
- Delegation preserves the chain: the token carries the subject and the acting party in the
actclaim. Impersonation erases it: the actor becomes indistinguishable from the subject. RFC 8693 supports both; only one of them is auditable. - Can token exchange convert an ID token into an access token (or vice versa)?
- Yes —
subject_token_typeandrequested_token_typeaccept both, in any combination, for impersonation and delegation alike. See the matrix above; the discipline is using each type for what it answers: access tokens for authorization, ID tokens for verifiable identity. - Can the exchanged token be a refresh token?
- The spec allows requesting different token types, but issuing refresh tokens to intermediaries extends delegation beyond the life of the original request. In service-to-service flows, prefer short-lived access tokens re-exchanged as needed.
- Does token exchange work with stateless JWTs in PingAM?
- Yes — and for high-throughput meshes it is usually the right choice. Accept the revocation trade-off explicitly and compensate with short TTLs.