Token exchange (RFC 8693) is the OAuth grant for transforming one token into another. You hand the platform a valid token and a request for a new one; the platform decides whether the swap is permitted and, if so, issues the new token.
This sounds abstract. Three concrete cases will make it click.
Case 1: down-scoping
Section titled “Case 1: down-scoping”Your service holds an access token with scopes users:read users:write audit:read. It needs to call a downstream service that only needs users:read. You do not want to forward the broader token — a bug in the downstream could misuse the extra capabilities.
Exchange the broad token for a narrower one:
POST https://<your-tenant-url>/oauth2/tokenContent-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=<the-broad-token>&subject_token_type=urn:ietf:params:oauth:token-type:access_token&scope=users:readThe response is a fresh access token with only users:read. Forward it to the downstream service. If the downstream is compromised, the blast radius is limited to what users:read allows.
Case 2: audience swap
Section titled “Case 2: audience swap”Service A holds a token whose audience is https://api-a.cymmetri.com. It needs to call Service B at https://api-b.cymmetri.com. Service B will reject the existing token because the aud claim is wrong.
Swap audiences:
POST https://<your-tenant-url>/oauth2/tokenContent-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=<service-a-token>&subject_token_type=urn:ietf:params:oauth:token-type:access_token&audience=https://api-b.cymmetri.com&scope=...The platform checks whether Service A (the subject of the original token) is allowed to call Service B (the new audience). If allowed, the new token is issued; if not, invalid_target comes back.
Case 3: act-as (impersonation)
Section titled “Case 3: act-as (impersonation)”A support engineer is signed in. They need to perform an action on behalf of a customer's user — a refund, an account recovery, a data lookup. The right token model is "the support engineer is acting as the customer's user" — both identities are visible in the audit log, neither is hidden.
Exchange the engineer's token for a customer-scoped token:
POST https://<your-tenant-url>/oauth2/tokenContent-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=<engineer-token>&subject_token_type=urn:ietf:params:oauth:token-type:access_token&actor_token=<customer-user-token>&actor_token_type=urn:ietf:params:oauth:token-type:access_token&scope=accounts:readThe platform issues a new access token whose sub is the customer's user, with an act claim recording the engineer's identity. Every API call carrying that token is traceable to both identities — the engineer who acted and the user they acted as. This is the right pattern for support tooling.
When NOT to use token exchange
Section titled “When NOT to use token exchange”- You just need a new token because the old one expired. That's refresh, not exchange.
- You are a backend calling another backend in a job that has no user context. That's client credentials.
- You are the same service calling the same downstream you've always called. No need to transform anything; just forward the token (assuming the audience is right).
Token exchange is for transformations. If nothing about the token is changing, you do not need this grant.
Authorization rules
Section titled “Authorization rules”Not every transformation is permitted. The tenant admin controls:
- Which application can perform exchanges at all (an opt-in flag on the application).
- Which audiences each application may target (a policy list).
- Whether act-as is permitted, and which subject types may be impersonated.
The platform refuses with invalid_target or invalid_grant when a request crosses a boundary the policy does not allow. The audit log records every exchange, allowed or denied.
Token type identifiers
Section titled “Token type identifiers”The full set of token types you might see:
| Identifier | Means |
|---|---|
urn:ietf:params:oauth:token-type:access_token | A standard OAuth access token |
urn:ietf:params:oauth:token-type:refresh_token | A refresh token |
urn:ietf:params:oauth:token-type:id_token | An OIDC id token |
urn:ietf:params:oauth:token-type:jwt | Any JWT |
You almost always exchange access tokens for access tokens. The other types come up in federation scenarios — for example, exchanging a SAML assertion for an access token at the trust boundary.
How tokens are linked in the audit log
Section titled “How tokens are linked in the audit log”Every exchange records:
- The original
subject_tokenjti. - The new token's jti.
- The
actorif present. - The application performing the exchange.
This lets you reconstruct a call chain after the fact: a single user request can hand off through three services, each performing a narrowing exchange, and the audit log shows the full lineage.