OAuth 2.1

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-urlencoded

Parameters

All parameters are sent as application/x-www-form-urlencoded.

ParameterRequiredDescription
grant_typeYesMust be urn:ietf:params:oauth:grant-type:token-exchange
subject_tokenYesThe token being exchanged (the inbound Bearer token)
subject_token_typeYesMust be urn:ietf:params:oauth:token-type:access_token
resourceRecommendedTarget audience URI for the new token (RFC 8707)
audienceOptionalAlternative to resource. When both are present, resource takes precedence
scopeOptionalSpace-separated subset of the subject token's scopes. If absent, all scopes are inherited
client_idYesYour client's public identifier
client_secretYesYour 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_secret

Authorization 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%3Aproducts

Response

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 codeHTTP statusWhen it occurs
invalid_request400subject_token or subject_token_type is missing or wrong
invalid_client401Client authentication failed or public client attempted exchange
invalid_grant400subject_token is unknown, expired, or revoked
unauthorized_client400Client does not have the token-exchange capability enabled
invalid_target400Requested resource/audience is not allowed for this client
invalid_scope400Requested 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 sub claim (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_id is 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.

On this page