Using Logto Token Exchange Grants for Delegated Authentication
Logto implements the OAuth 2.0 Token Exchange grant (RFC 8693) in its core OIDC provider, allowing clients to exchange existing access tokens for new tokens scoped to downstream services by enabling the token_exchange_enabled flag and calling the token endpoint with grant_type=urn:ietf:params:oauth:grant-type:token-exchange.
Logto is an open-source identity and access management (IAM) platform that supports delegated authentication through the OAuth 2.0 Token Exchange grant. As implemented in the logto-io/logto repository, this feature allows applications to securely exchange subject tokens for new access tokens with different audiences or scopes. This article examines the source code implementation and provides practical examples for configuring and using token exchange in your Logto deployment.
How Token Exchange Works in Logto
Logto’s token exchange implementation follows the RFC 8693 specification while integrating deeply with the existing OIDC provider architecture.
Grant Type Registration
In packages/core/src/libraries/oidc-provider.ts, the OIDC provider configuration registers the Token Exchange grant type by including urn:ietf:params:oauth:grant-type:token-exchange in the grantTypes array. This registration makes the grant available at the tenant's token endpoint (/oidc/token).
Subject Token Validation
When a request arrives with grant_type=urn:ietf:params:oauth:grant-type:token-exchange, the handler in packages/core/src/libraries/token-exchange.ts validates the subject_token. The handler verifies the token's signature, expiration, and original scopes to ensure it is eligible for exchange. It also checks that the client's metadata includes token_exchange_enabled: true as defined in packages/core/src/libraries/client.ts.
Token Generation and Signing
After validation, Logto generates a new access token using the JWT utilities in packages/core/src/libraries/jwt-customizer.ts. The new token is signed with the tenant's current signing key managed in packages/core/src/tenants/signing-key-rotation-state.ts. The generated token includes the requested scope and aud (audience) claims based on the resource parameter provided in the exchange request.
Event Emission
For audit and extensibility, Logto emits a grant.token-exchange event via the event-listener system in packages/core/src/event-listeners/grant.ts. This allows inline hooks to enforce additional policies or logging when exchanges occur.
Enabling Token Exchange for Clients
Before a client can initiate a token exchange, you must enable the capability in the client's configuration. Set the token_exchange_enabled property to true using the Logto Admin API or Management SDK.
// Example using Logto Management SDK
await logtoClient.applications.updateApplication(clientId, {
token_exchange_enabled: true
});
This configuration is persisted in packages/core/src/libraries/client.ts and verified by the token exchange handler before processing any exchange requests.
Requesting a Token Exchange
To exchange an existing access token for a new one with different scopes or audience, send a POST request to the token endpoint with the following parameters:
curl -X POST https://<your-logto-domain>/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "subject_token=EXISTING_ACCESS_TOKEN" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
-d "requested_token_type=urn:ietf:params:oauth:token-type:access_token" \
-d "scope=read:profile write:profile" \
-d "resource=api://downstream-service"
The handler in packages/core/src/libraries/token-exchange.ts processes this request and returns a JSON response containing the new access token.
Handling the Exchange Response
The token endpoint returns a standard OAuth 2.0 response with the exchanged token:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:profile write:profile"
}
This new token is bound to the requested resource (api://downstream-service) and can be used to authenticate requests to that specific API. The downstream service validates the token using Logto's JWKS endpoint (/oidc/.well-known/jwks.json).
Implementing Custom Validation Hooks
You can implement custom logic to validate or log token exchange events by creating an inline hook script that listens for the grant.token-exchange event:
// Inline hook script configured in Logto Console
module.exports = async (event) => {
if (event.type === 'grant.token-exchange') {
const { client, subjectToken, requestedScope } = event.payload;
// Enforce custom policy
if (!client.roles.includes('token_exchange_allowed')) {
throw new Error('Client not authorized for token exchange');
}
// Log the exchange for audit purposes
console.log(`Token exchanged for client ${client.id} with scope ${requestedScope}`);
}
};
This hook runs whenever the event listener in packages/core/src/event-listeners/grant.ts emits the exchange event, allowing you to enforce business-specific security policies.
Summary
- Logto token exchange grants implement RFC 8693 to enable delegated authentication between services.
- Enable the feature per-client by setting
token_exchange_enabled: trueinpackages/core/src/libraries/client.ts. - The grant handler in
packages/core/src/libraries/token-exchange.tsvalidates subject tokens and generates new JWTs usingpackages/core/src/libraries/jwt-customizer.ts. - Exchange requests require
grant_type=urn:ietf:params:oauth:grant-type:token-exchangeand specify the targetresourceandscope. - The
grant.token-exchangeevent inpackages/core/src/event-listeners/grant.tsenables custom validation hooks.
Frequently Asked Questions
What is the token exchange grant type in Logto?
The token exchange grant type is urn:ietf:params:oauth:grant-type:token-exchange, implementing RFC 8693. It allows a client to exchange an existing access token (subject token) for a new token with different scopes or audience, stored and validated in packages/core/src/libraries/token-exchange.ts.
How do I enable token exchange for a Logto application?
You must update the client configuration to set token_exchange_enabled: true via the Management API or SDK. This flag is checked by the exchange handler in packages/core/src/libraries/client.ts before processing any exchange requests.
Can I exchange a refresh token instead of an access token?
While the examples show access token exchange (subject_token_type=urn:ietf:params:oauth:token-type:access_token), the implementation in packages/core/src/libraries/token-exchange.ts supports various token types. Check the specific token type URN supported by your Logto version, typically allowing access tokens as subject tokens.
How do downstream services validate exchanged tokens?
Downstream services validate exchanged tokens using the standard JWKS endpoint at /oidc/.well-known/jwks.json. The new token is signed with the tenant's key from packages/core/src/tenants/signing-key-rotation-state.ts and contains the requested audience claim (aud) matching the resource parameter from the exchange request.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →