# Using Logto Token Exchange Grants for Delegated Authentication

> Leverage Logto's OAuth 2.0 Token Exchange grant for secure delegated authentication. Securely exchange tokens for downstream services by enabling the token exchange feature.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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.

```javascript
// 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`](https://github.com/logto-io/logto/blob/main/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:

```bash
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`](https://github.com/logto-io/logto/blob/main/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:

```json
{
  "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`](https://github.com/logto-io/logto/blob/main//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:

```javascript
// 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`](https://github.com/logto-io/logto/blob/main/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: true` in [`packages/core/src/libraries/client.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/client.ts).
- The grant handler in [`packages/core/src/libraries/token-exchange.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/token-exchange.ts) validates subject tokens and generates new JWTs using [`packages/core/src/libraries/jwt-customizer.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/jwt-customizer.ts).
- Exchange requests require `grant_type=urn:ietf:params:oauth:grant-type:token-exchange` and specify the target `resource` and `scope`.
- The `grant.token-exchange` event in [`packages/core/src/event-listeners/grant.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/event-listeners/grant.ts) enables 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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main/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`](https://github.com/logto-io/logto/blob/main//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`](https://github.com/logto-io/logto/blob/main/packages/core/src/tenants/signing-key-rotation-state.ts) and contains the requested audience claim (`aud`) matching the `resource` parameter from the exchange request.