Token Exchange

Token exchange (RFC 8693) lets a client trade a subject token it already holds for a new token scoped to a downstream Resource or Connection. It is how an Inbound App or Agentic Client narrows or retargets an existing credential without sending the user through login again.

Token exchange is not an Inbound App setting. Every Descope project exposes it at the shared token endpoint. What actually controls whether an exchange succeeds is a Policy whose grant type includes Delegated access (token exchange).

Authentication grants vs token exchange

Inbound Apps use two different layers of OAuth behavior. Keep them separate when you read the docs or configure the Console:

LayerWhat it controlsWhere you configure it
Authentication grantsHow a client obtains its first token (login, M2M, external JWT, etc.)Inbound App Grant Types toggles in the Console
Token exchangeHow a client retargets a token it already has to a specific Resource or ConnectionPolicies only — no per-app toggle

Always on at the project level

Any client that can call /token may send grant_type=urn:ietf:params:oauth:grant-type:token-exchange. Descope does not require you to enable token exchange per Inbound App. The exchange succeeds only when an active policy permits it.

Typical sequence:

  1. The client authenticates with an authentication grant (authorization code, client credentials, JWT bearer, etc.) and receives a subject token.
  2. The client calls /token again with the token-exchange grant, naming the target Resource (or Connection) and optional scopes.
  3. Descope evaluates Policies. If a matching rule allows Delegated access (token exchange) for that subject, target, and scopes, Descope returns the new token. Otherwise the request is denied and logged as a policy violation.

Unlike user-delegated authentication grants, token exchange never shows a consent screen. Policy is the only gate.

Configure access with Policies

Create or update a policy on the Policies page:

  1. Subjects — Select the Inbound App or Agentic Client that may perform the exchange (or define custom conditions on client tags, tenant, etc.).
  2. Targets — Choose the Resource or Connection and the scopes this policy grants.
  3. Grant types — Enable Delegated access (token exchange).

See Policies → Grant Types for the full model and examples such as tenant-scoped delegated access.

Least privilege

A policy can only grant scopes that already exist on the target Resource or Connection. The token Descope issues is trimmed to the intersection of what the policy allows, what the target defines, and what the client requested.

Exchange for a Resource token

Use this when a client already holds a Descope access token and needs a separate token for your API or MCP server — different aud and/or scope than the original.

1Authenticate for the first Resource
2Exchange for a different Resource

The client keeps its original token. To reach a different Resource — one with a different audience or scope set — it posts a token exchange request with the existing token as subject_token, plus the target resource URI and requested scopes. Descope issues a new JWT scoped only to that Resource.

Request

Send POST to the token endpoint:

ParameterDescription
grant_typeurn:ietf:params:oauth:grant-type:token-exchange
subject_tokenThe access token (or other token type) being exchanged
subject_token_typeUsually urn:ietf:params:oauth:token-type:access_token
resourceTarget Resource URI (RFC 8707)
scopeOptional subset of scopes; must be allowed by policy
Client authenticationclient_id / client_secret, or other method configured on the app
curl -X POST "__BaseURL__/oauth2/v1/apps/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "client_id=<CLIENT_ID>" \
  -d "client_secret=<CLIENT_SECRET>" \
  -d "subject_token=<SUBJECT_TOKEN>" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "resource=https://your-api.example.com" \
  -d "scope=orders.read"

Your Resource validates the returned Bearer token (iss, aud, exp, scope) like any other Inbound App token. See Developing APIs and Resources → Using a Resource Token.

Exchange for a Connection credential

When an agent or MCP server needs to call a third-party API, token exchange can produce a Connection-scoped credential from the Connections vault. The policy target is the Connection, not your own Resource.

That pattern is common in agentic architectures where your MCP server holds a Descope token and exchanges it at runtime for downstream credentials. See Downstream credential access for the full flow.

When to use token exchange

ScenarioSee also
User signed in once; client now needs a scoped token for one API or MCP serverResources, MCP server
MCP gateway exchanging an inbound token for a Connection or another ResourceMCP Gateways, Downstream credential access
Autonomous agent with M2M token needs a narrower Resource tokenClient credentials
Mint an ID-JAG for a third-party resource serverEnterprise-managed authorization
Runtime fetch of a vaulted third-party credentialDownstream credential access

Token exchange is not a substitute for the initial authentication grant. The client must already hold a valid subject token from authorization code, client credentials, JWT bearer, CIBA, or another supported grant.

Denials and troubleshooting

When no policy permits the requested subject, target, or scopes, Descope denies the exchange at the token boundary — no token is returned. Each denial is recorded as a Warning audit event with the client, target, requested scopes, and evaluation outcome.

Common fixes:

  • Add or update a policy with Delegated access (token exchange) for the client and Resource (or Connection).
  • Confirm the Resource or Connection defines the scopes you are requesting.
  • Ensure the subject token is still valid and was issued to the same client (or satisfies your policy's subject conditions).

Monitor aggregated violations in the Agentic Activity Dashboard.

Was this helpful?

On this page