Configure token exchange for backend authentication
This guide shows you how to configure token exchange, which allows MCP servers to authenticate to backend APIs using short-lived, properly scoped tokens instead of embedded secrets.
For conceptual background on how token exchange works and its security benefits, see Backend authentication, which includes a sequence diagram illustrating the complete flow.
Prerequisites
Before you begin, make sure you have:
- ToolHive installed and working
- Basic familiarity with OAuth, OIDC, and JWT concepts
- An identity provider that supports RFC 8693 token exchange (such as Okta, Auth0, or Keycloak)
- A backend service configured to accept tokens from your identity provider
- Familiarity with Authentication and authorization setup in ToolHive
From your identity provider, you'll need:
- Audience value for the MCP server
- Issuer URL
- JWKS URL (for key verification)
- Token exchange endpoint URL
- Client credentials for the token exchange client
Configure your identity provider
Token exchange requires your identity provider to issue tokens for the backend service when presented with a valid MCP server token. The exact configuration steps vary by provider, but generally include:
Register a token exchange client
Create an OAuth application in your identity provider for ToolHive to use when performing token exchange:
- Note the client ID and client secret
- Grant the application permission to use the
urn:ietf:params:oauth:grant-type:token-exchangegrant type
Token exchange is an authenticated flow—ToolHive uses these credentials to prove its identity when requesting exchanged tokens from the identity provider.
Create an API Services application for ToolHive and enable the token exchange grant type in the application settings.
Define audience and scopes for the backend service
Configure your identity provider to recognize the backend service:
- Define the audience value that identifies your backend service (for example,
backend-api) - Specify the scopes the backend service accepts (for example,
api:read,api:write)
Create a custom authorization server for the backend service and define the scopes under Security > API > Authorization Servers.
Create an access policy
Set up a policy that permits token exchange and controls what scopes are included in exchanged tokens:
- Enable the token exchange grant type for the ToolHive client
- Define which users or groups can obtain tokens for the backend service
- Specify the scopes included in exchanged tokens
Add a trust relationship from the MCP authorization server to the backend authorization server, then create access policies on the backend server to permit token exchange.
Consult the Okta token exchange documentation for detailed steps.
MCP server requirements
The MCP server that ToolHive fronts must accept a per-request authentication token. Specifically, the server should:
- Read the access token from the
Authorization: Bearerheader (or a custom header if you configure--token-exchange-header-name) - Use this token to authenticate to the backend service
- Not rely on hardcoded secrets or environment variables for backend authentication
ToolHive injects the exchanged token into each request, so the MCP server receives a fresh, properly scoped token for every call.
Run an MCP server with token exchange
Once your identity provider is configured, start your MCP server with token exchange enabled:
thv run \
--oidc-audience <MCP_AUDIENCE> \
--oidc-issuer <ISSUER_URL> \
--oidc-jwks-url <JWKS_URL> \
--token-exchange-url <TOKEN_EXCHANGE_ENDPOINT> \
--token-exchange-client-id <TOOLHIVE_CLIENT_ID> \
--token-exchange-client-secret <TOOLHIVE_CLIENT_SECRET> \
--token-exchange-audience <BACKEND_AUDIENCE> \
--token-exchange-scopes <BACKEND_SCOPES> \
<server-name>
Parameter reference
| Parameter | Description |
|---|---|
--oidc-* | Standard OIDC parameters for validating incoming client tokens |
--token-exchange-url | Your identity provider's token exchange endpoint |
--token-exchange-client-id | Client ID for ToolHive to authenticate to the IdP |
--token-exchange-client-secret | Client secret for ToolHive (or use --token-exchange-client-secret-file) |
--token-exchange-audience | Target audience for the exchanged token (your backend service) |
--token-exchange-scopes | Scopes to request for the backend service |
Optional parameters:
--token-exchange-header-name: Custom header name for injecting the exchanged token (defaults to replacing theAuthorizationheader)--token-exchange-subject-token-type: Type of subject token to exchange; useid_tokenfor Google STS (defaults toaccess_token)
Verify the configuration
To confirm token exchange is working:
- Connect to the MCP server with a client that supports authentication
- Make a tool call that requires backend access
- Check that the request succeeds and the backend receives a properly scoped token
You can also verify by examining your identity provider's logs for successful token exchange requests, or by checking audit logs on your backend service to confirm requests arrive with the correct user identity and scopes.
Example: Okta configuration
This example shows a complete configuration for an MCP server that connects to a backend API, using Okta for token exchange:
thv run \
--oidc-audience mcp-server \
--oidc-issuer https://dev-123456.okta.com/oauth2/aus1234567890 \
--oidc-jwks-url https://dev-123456.okta.com/oauth2/aus1234567890/v1/keys \
--token-exchange-url https://dev-123456.okta.com/oauth2/aus9876543210/v1/token \
--token-exchange-client-id 0oa0987654321fedcba \
--token-exchange-client-secret-file /path/to/client-secret \
--token-exchange-audience backend-api \
--token-exchange-scopes "api:read,api:write" \
<server-name>
Key points in this example:
- Two authorization servers: The
--oidc-issuerURL (aus1234567890) is the MCP authorization server that validates incoming client tokens. The--token-exchange-urluses a different authorization server (aus9876543210) that issues tokens for the backend API. - Audience transformation: Client tokens arrive with audience
mcp-server. ToolHive exchanges them for tokens with audiencebackend-api, which the backend service expects. - Scope transformation: The original token has MCP-specific scopes (like
mcp:tools:call), while the exchanged token has backend-specific scopes (api:read,api:write). The user's identity is preserved, but the permissions are transformed for the target service.
Related information
- Backend authentication - conceptual overview of token exchange and federation
- Authentication and authorization - basic auth setup for MCP servers
- CLI reference for
thv run- complete list of flags