Downstream Credential Access
When a service holds an inbound Descope access token and needs to call a different downstream API, it usually needs a credential scoped to that target, not the token it already has. That applies to MCP servers fetching third-party OAuth tokens, MCP tools calling internal APIs registered as separate Resources, and any middleware that brokers access on an agent's behalf.
This page covers the server-side pattern: validate the inbound token, exchange it at the Descope STS (RFC 8693), and call the downstream service with the outbound credential.
For the MCP-specific walkthrough (tool handlers, sequence diagrams, and Python SDK shortcuts), see Calling downstream APIs from MCP tools.
Two Tokens: Inbound and Outbound
Every exchange involves two tokens, and keeping them straight makes the rest of this page easier to follow:
- Inbound token: the access token your service receives from the agent or MCP client. Its
audis your service (your Resource), and it carries who is calling and the scopes they consented to. You validate it, but you never forward it downstream. - Outbound credential: what Descope hands back for the downstream call. It is either a Connection token (an OAuth token or API key from the Connections vault) or a new Resource token (a Descope JWT minted with a different audience, for another Descope Resource). This is what you actually send downstream.
Choose a Path
| Downstream target | Pattern | What you receive |
|---|---|---|
| A Descope Resource that accepts the inbound token's audience and scopes | Passthrough | Use the inbound access token as-is |
| An internal API registered as a different Descope Resource | STS → Resource | A Descope JWT scoped to that resource's aud and scopes |
| A third-party OAuth service or API-key-based service | STS → Connection | The stored OAuth access token or API key from Connections |
Passthrough applies when the downstream service is the same Resource the caller already has a token for, or when its audience and scope requirements match the inbound token. For most tool handlers you should exchange the token rather than forward it. Passthrough falls short when:
- The inbound token's
audis your service, but the downstream target expects its own audience or a Connection token from the Connections vault, not your token. - Policy is evaluated once at your ingress, so you cannot apply per-target rules (the user, the tenant, the calling client, and your server) at the downstream hop.
- The audit log records no distinct exchange event for each downstream target the tool reached.
- The full inbound scope set is exposed downstream instead of a credential downscoped for that call.
STS → Resource and STS → Connection use the same token exchange request. The resource parameter tells Descope which path to take. See Using a Resource Token for Resource-to-Resource exchange, and Fetching Connection Tokens for Connection retrieval APIs and SDK methods.
Your Server Is Both a Resource and a Client
To broker downstream access, a single server plays two Descope roles at the same time. It is both a Resource and a Client:
- As a Resource (the inbound side), it receives the caller's access token, whose
audis your service URL, and validates that JWT against Descope's JWKS. - As a Client (the outbound side), it authenticates to the STS with its own
client_idandclient_secretto exchange the inbound token for the outbound credential.
The same process wears both hats:
The separate client ID (within the server) tells the STS which system is making the exchange, so that:
- Policies evaluate against the full context, covering the original user, the calling client, and your server
- The audit log records the complete delegation chain: user → calling client → your server → downstream service
- Policies can target your server specifically using
client.tagsorclient.name
Without a registered client, the STS cannot distinguish a legitimate server-side exchange from a caller trying to fetch credentials directly.
Setup
1. Register a Client for Your Server
In the Descope Console, go to Clients and create a client to represent the service making exchanges.
- Enable the Client Credentials grant type
- Disable grant types the server will not use
- Copy the generated Client ID and Client Secret
This client is separate from the agents or MCP clients that call your service. It represents the server itself acting as an OAuth client toward the STS.
2. Store Server Credentials Securely
Store the client ID and secret in your server's environment (or secrets manager). These credentials authenticate every token exchange request.
3. Define What the Server Can Reach
Register each downstream target your code will exchange for:
| Downstream target | Where to configure it | What the STS returns |
|---|---|---|
| Internal API that validates Descope JWTs | Connect → Resources as an API Resource | A Resource-scoped Descope JWT |
| Third-party OAuth or API-key service | Agentic Identity Hub → Connections | The OAuth token or API key stored in the Connections vault |
MCP scopes → Connection scopes
MCP Server Resource scopes are what a user consents to when they authorize your MCP server (for example mcp:read_hubspot_contacts). In the MCP server's scopes config, you map each of those scopes to the downstream Connection scopes it needs (for example crm.objects.contacts.read).
Descope uses that mapping at two points, and you never pick Connection scopes by hand at either:
- During the connect: when the user links the Connection, Descope requests exactly the Connection scopes that correspond to the MCP server scopes the user consented to, so the token stored in the Connections vault carries the right scopes.
- At fetch time: when your server fetches the token, Descope reads the scopes on the access token (the same MCP server resource scopes) and automatically returns the stored Connection token with the matching Connection scopes.

Configure this mapping in your MCP server configuration underneath the Scopes section, not on the Policies page.
See MCP Server Resource scopes and MCP server settings for more information.
4. Create a Policy for Who Can Access
The scope mapping in step 3 lives on the MCP server config. A Policy is a separate control for who is allowed to reach a target: which agents or clients can access which Resources, and what your server, acting as a client, is allowed to exchange for.
Descope evaluates the policy during token exchange and when fetching a Connection token from the vault with an access token. Without a matching allow policy, the request is denied even when the scope mapping is correct.
Making the Exchange
At request time your server validates the inbound access token, then calls the Descope token endpoint as its registered client. The same request shape covers both STS → Resource and STS → Connection; the resource parameter selects the target.
POST /oauth2/v1/token
Authorization: Basic {base64(client_id:client_secret)}
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token={inbound_access_token}
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&resource={resource_url_or_connection_id}Descope validates your server's credentials, evaluates policy against the inbound token's claims, and returns the outbound credential: either a resource-scoped Descope JWT or a token from the Connections vault.
See the Token API for full parameter reference, including optional scope narrowing and resource indicators.
Audit Trail
Every exchange produces an audit event that records:
- The original user identity from the inbound token
- The calling client that initiated the request
- Your server's client ID that made the exchange
- The downstream Resource or Connection accessed
- The policy decision applied
This gives you a traceable record from the user who authorized access, through the agent or client that requested it, to the server that fetched the outbound credential.