Token Exchange (RFC 8693)
Exchange a valid access token for a new one bound to a different audience or a reduced scope set, without changing the resource owner.
The token exchange grant (RFC 8693) lets a registered confidential client — typically a gateway or proxy — exchange an inbound access token for a new one suited to a specific upstream resource. This is the standard mechanism for gateway-to-upstream delegation and is required whenever a proxy must call a downstream API on behalf of the token's original owner.
When to use this
Use token exchange when a service that receives a Bearer token needs to call an upstream API with a different audience, or needs to reduce the scope set before forwarding. The original resource owner (sub) is always preserved — the new token remains tied to the same user and store, just with a different audience and/or fewer scopes.
Do not use this to elevate privileges. Requesting a scope that was not in the original token is always rejected.
Endpoint
POST https://app.whatalo.com/oauth/token
Content-Type: application/x-www-form-urlencodedParameters
All parameters are sent as application/x-www-form-urlencoded.
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be urn:ietf:params:oauth:grant-type:token-exchange |
subject_token | Yes | The token being exchanged (the inbound Bearer token) |
subject_token_type | Yes | Must be urn:ietf:params:oauth:token-type:access_token |
resource | Recommended | Target audience URI for the new token (RFC 8707) |
audience | Optional | Alternative to resource. When both are present, resource takes precedence |
scope | Optional | Space-separated subset of the subject token's scopes. If absent, all scopes are inherited |
client_id | Yes | Your client's public identifier |
client_secret | Yes | Your client's secret (via HTTP Basic or body) |
Client authentication
Token exchange requires confidential client authentication. Public clients (token_endpoint_auth_method: "none") are not permitted to use this grant.
Authenticate using HTTP Basic (recommended):
Authorization: Basic BASE64(client_id:client_secret)Or via body parameters:
client_id=your_client_id&client_secret=your_client_secretAuthorization requirements
Your client must be explicitly enabled for token exchange by a Whatalo administrator. This is controlled via an internal flag (metadata.allow_token_exchange: true) on your client registration. Contact support to request this capability for your client.
Additionally, if you request a resource (audience) different from the subject token's original audience, that specific audience must be included in your client's allowed audience list (metadata.allowed_token_exchange_audiences). You may always request the same audience as the subject token (a downscope-only operation) without a separate allowlist entry.
Example request
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(your_client_id:your_client_secret)
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&subject_token=<inbound_access_token>
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token
&resource=https%3A%2F%2Fapi.whatalo.com
&scope=read%3Astore+read%3AproductsResponse
A successful exchange returns HTTP 200 with this body:
{
"access_token": "new_opaque_token_value",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:store read:products"
}No refresh_token is issued for token-exchange grants in the current version. The returned access token has a 1-hour lifetime. Re-exchange using the original flow if a new token is needed after expiry.
Error responses
| Error code | HTTP status | When it occurs |
|---|---|---|
invalid_request | 400 | subject_token or subject_token_type is missing or wrong |
invalid_client | 401 | Client authentication failed or public client attempted exchange |
invalid_grant | 400 | subject_token is unknown, expired, or revoked |
unauthorized_client | 400 | Client does not have the token-exchange capability enabled |
invalid_target | 400 | Requested resource/audience is not allowed for this client |
invalid_scope | 400 | Requested scope exceeds the scopes in the subject token |
Security notes
- No scope escalation. You can only request scopes that are already present in the subject token. Requesting broader access is always rejected with
invalid_scope. - Resource owner preserved. The
subclaim (the user) of the new token is always the same as in the subject token. This grant does not change ownership. - Caller identity stamped. The new token's
client_idis your client (the one performing the exchange), not the original client that obtained the subject token. This makes delegation auditable. - Audience must be explicitly allowed. Requesting an audience not in your client's allowlist is rejected with
invalid_target(RFC 8707 §2.2), preventing unauthorized cross-resource token reuse.
Well-known advertisement
The Authorization Server advertises support for this grant in its discovery document:
{
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:token-exchange"
]
}See the Discovery page for the full metadata document.