Token exchange example calls and responses
Example token exchange requests and responses demonstrate common scenarios, including scope reduction, audience changes, delegation, and claim mapping.
For instructions on configuring the client, including allowed exchange types, authorization servers, audiences, resources, scopes, and claim mapping, see OIDC client (using the token exchange mechanism).
Scope reduction for downstream services
An application receives a broadly scoped access token from the login flow. Before calling a downstream microservice, it exchanges the token for one with a reduced set of scopes, following the principle of least privilege.
Configuration:
- Allowed exchange: Delegation or impersonation.
- Scopes: Restrict to
read:orders.
Token exchange request
Original token with read:orders and read:profile scopes:
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <client_credentials>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJhbGciOi...originalToken...
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&scope=read:read:orders read:profile
Response
New token with only read:orders scope:
{
"access_token": "eyJhbGciOi...reducedScopeToken...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:orders"
}Audience change
A frontend application receives a token scoped for its own API (api.frontend.com). Before calling a partner service (api.partner.com), it exchanges the token for one with a new aud value.
Configuration
- Allowed audiences: api.partner.com
Token exchange request
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <client_credentials>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJhbGciOi...originalToken...
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=api.partner.com
Response
{
"access_token": "eyJhbGciOi...newAudienceToken...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
}Decoded JWT payload
{
"sub": "user_123",
"aud": "api.partner.com",
"scope": "",
"iss": "{issuer}",
"iat": 1724487400,
"exp": 1724491000
}Delegation (act-as)
A support dashboard application allows agents to act on behalf of customers to perform troubleshooting. The agent’s client uses token exchange to produce a delegated token with an `act' claim representing the customer.
Policy settings
- Allowed exchange type: Delegation.
- Allowed authorization servers: Must accept both subject and actor tokens from trusted issuers.
- Scope handling: May be restricted to limited scopes (e.g.,
support:read). - Claim mapping: Optional, policies can forward or transform claims from either token.
Token inputs
Subject token (Decoded JWT Payload)
Represents the end user (customer):
{
"sub": "user_123",
"email": "[email protected]",
"tenant_id": "acme",
"iss": "https://login.strivacity.com",
"iat": 1724487600,
"exp": 1724491200
}
Actor Token (Decoded JWT payload)
Represents the support agent:
{
"sub": "support_agent_456",
"email": "[email protected]",
"role": "support",
"iss": "https://login.strivacity.com",
"iat": 1724487650,
"exp": 1724491250
}Token exchange request
Request includes the JWT subject token and actor token to use in the resulting delegated token.
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <client_credentials>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=eyJhbGciOi...subjectToken...&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
actor_token=eyJhbGciOi...actorToken...&
actor_token_type=urn:ietf:params:oauth:token-type:access_token
Response (token exchange output)
Response is a bearer token with an access token in the JWT payload:
{
"access_token": "eyJhbGciOi...delegatedAccessToken...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600
}Resulting access token (decoded JWT payload)
The resulting access token includes the support agent’s identifier as the root “sub” claim and the customer's identifier in the “sub” claim under the “act” claim.
{
"sub": "support_agent_456",
"scope": "support:read user:read",
"tenant_id": "acme",
"act": {
"sub": "user_123"
},
"iss": "https://login.strivacity.com",
"iat": 1724487700,
"exp": 1724491300
}Claim mapping
Claim mapping allows you to modify or enrich the token claims in the resulting token during exchange. The claim mapping code block allows for mapping data into the new token from:
- The original token
- From an identity store
- From a 3rd party data source
Response 200 from claim mapping
Claim mapping code
module.exports = async function ({ request_body, requested_scopes, account }) {
return new ClaimMapping({ custom: "data" });
}; Content of the token in response
"custom": "data" appears at top level
{
"aud": [
"hodor"
],
"client_id": "d3b7292ee8594f97a255d7c7c0221071",
"custom": "data",
"exp": 1753346210,
"ext": {
"given_name": "2188e297-780e-4ed4-b342-706378def084"
},
"given_name": "2188e297-780e-4ed4-b342-706378def084",
"iat": 1753342610,
"iss": "https://demo.strivacity.cloud/",
"jti": "74873dfa-5c20-4d43-b64e-3e0c9d0117b2",
"may_act": {
"sub": "d3b7292ee8594f97a255d7c7c0221071"
},
"nbf": 1753342610,
"scp": [Updated 7 minutes ago

