Skip to main content
The On-Behalf-Of (OBO) Token Exchange (RFC 8693) enables middle-tier services to preserve user identity and permissions when calling downstream APIs. When an application needs to call a downstream API, it can use:
  • Client Credentials Flow: The application acts on its own behalf and authenticates as itself. The request may have been initiated by a user, but that context will be lost. The downstream service only knows the identity of the calling application.
  • On-Behalf-Of (OBO) Token Exchange: The application receives a user-scoped token and can exchange it for a new token to call downstream services. This preserves the identity and context of the original end user throughout the call chain.
For example, if a user triggers a call to Service A, which then calls Service B, OBO token exchange allows Service A to exchange the user’s access token for a new token that:
  • Maintains the original user’s identity and permissions
  • Is scoped specifically for Service B
  • Enables Service B to make authorization decisions based on the end user
OBO token exchanges trigger the post-login Action trigger, where: Similar to a standard login flow, the scopes returned for downstream API calls are based on the user’s Role-Based Access Control (RBAC) policies.
When you purchase the Auth0 for AI Agents add-on, you can use your subscription tier’s maximum Authentication API rate limit for OBO token exchanges. For example, if you are on Private Cloud 100 RPS, you can exceed the OBO token exchange rate limit of 30 RPS and leverage the full 100 RPS capacity for your OBO token exchange requests. The Authentication API limit is shared and acts as the global ceiling for all Authentication API requests, including logins, token refreshes, and token exchanges combined. Reach out to your Technical Account Manager for more information.

Use cases

Common use cases for the OBO Token Exchange include:
  • MCP servers that need to call first-party APIs on the user’s behalf
  • Microservices that need to call downstream services on the user’s behalf
To enable your applications to call third-party APIs on the user’s behalf, use Token Vault.

How it works

OBO token exchange enables middle-tier services to exchange an incoming user token for a new token scoped to a downstream service. The new token preserves the original user’s identity while tracking the chain of services involved in the JSON Web Token (JWT) payload.

Example: MCP server calls first-party API

A user authenticates with Auth0 to a client application, which then calls an MCP server, which in turn needs to call a first-party API.

Step 1: User authentication

When the user logs in, Auth0 issues an access token scoped for the MCP server with the following claims in the JWT payload:
{
  "sub": "auth0|user123",
  "aud": "https://mcp-server.example.com",
  "azp": "spa_client_id" // or "client_id" depending on token dialect
}
ClaimValueDescription
subauth0|user123The end-user’s identity
audhttps://mcp-server.example.comToken scoped for the MCP server
azp (or client_id depending on the Access token profile)spa_client_idThe client application that requested the token

Step 2: OBO exchange

Using the OBO token exchange, the MCP server presents the user’s token to Auth0 and requests an access token scoped to the first-party API. Auth0 issues a new access token scoped for the API with the following claims:
{
  "sub": "auth0|user123",
  "aud": "https://first-party-api.example.com",
  "azp": "mcp_server_client_id", // or "client_id" depending on token dialect
  "act": {
    "sub": "mcp_server_client_id",
    "act": {
      "sub": "spa_client_id"
    }
  }
}
ClaimValueDescription
subauth0|user123Same user identity preserved
audhttps://first-party-api.example.comToken scoped for the first-party API
azp (or client_id depending on the Access token profile)mcp_server_client_idClient that requested the token (the MCP server that performed the exchange)
act{"sub": "mcp_server_client_id", "act": {"sub": "spa_client_id"}}Delegation chain showing all actors involved

The act claim

The act (actor) claim tracks the complete delegation chain. Each act level represents a service in the call chain, with the outermost act.sub identifying the current actor that performed the token exchange. In our example:
  • Outermost act.sub: mcp_server_client_id (the MCP server that just exchanged the token)
  • Nested act.sub: spa_client_id (the original client application)
The azp claim should match the outermost act.sub value, identifying the service that most recently performed the token exchange. If the first-party API calls another downstream service (https://calendar-api.acme.com), the delegation chain would extend:
{
  "sub": "auth0|user123",
  "aud": "https://calendar-api.acme.com",
  "azp": "first_party_api_client_id",
  "act": {
    "sub": "first_party_api_client_id",
    "act": {
      "sub": "mcp_server_client_id",
      "act": {
        "sub": "spa_client_id"
      }
    }
  }
}
The delegation chain is limited to five nested levels. The OBO token exchange will fail if the subject token already has five nested act levels.
400 Bad Request
{
  "error": "invalid_request",
  "error_description": "Delegation chain (`act` claim) depth exceeds the maximum allowed limit of 4"
}
Cache access tokens for the lifetime of the token instead of requesting a new token for each API call. Access tokens are reusable until they expire; repeated token exchanges waste resources, increase latency, and may trigger rate limits.

User > MCP server > API flow

The following diagram shows an end-to-end OBO token exchange flow where an MCP server calls a first-party API on the user’s behalf:
  1. User authentication: The user authenticates with the client application. The Auth0 Authorization Server issues Token A, scoped for the MCP server.
  2. Initial request: The client application calls the MCP Server, passing Token A in the Authorization: Bearer header.
  3. Validation and token exchange: The MCP server receives Token A, validates it, and passes it to the Auth0 Authorization Server’s /oauth/token endpoint. Using the OBO token exchange, the MCP server presents Token A as the subject_token and requests a new token for the first-party API.
  4. Token issuance: The Auth0 Authorization Server issues Token B. Token B has the same sub (user ID) as Token A, but the aud (audience) is now the first-party API.
  5. Downstream call: The MCP Server calls the first-party API using Token B. The API validates Token B and sees that the request is legitimately being made “on behalf of” the original user.

User > API1 > API2 > API3

The following diagram shows an end-to-end flow of a chain of microservices making downstream calls on the user’s behalf:
  1. User authentication: The user successfully authenticates with a client application. The Auth0 Authorization Server issues Token A, scoped for API1.
  2. Initial request: The client application calls API1, passing Token A in the Authorization: Bearer header.
  3. API1 delegates to API2: API1 receives Token A, validates it, then passes it to the Auth0 Authorization Server’s /oauth/token endpoint. Using the OBO token exchange, API1 presents Token A as the subject_token and requests a new token for API2.
  4. Token issuance: The Auth0 Authorization Server grants a new access token, Token B, to API1. Token B has the same sub (user ID) as Token A, but the aud (audience) is now API2.
  5. Downstream call: API1 makes a request to API2 using Token B.
  6. API2 delegates to API3: API2 receives Token B, validates it, then passes it to the Auth0 Authorization Server’s /oauth/token endpoint. Using the OBO token exchange, API2 presents Token B as the subject_token and requests a new token for API3.
  7. Token issuance: The Auth0 Authorization Server grants a new access token, Token C, to API2. Token C has the same sub (user ID) as Token A and B, but the aud (audience) is now API3.
  8. Downstream call: API2 makes a request to API3 using Token C. API3 validates Token C and sees that the request is legitimately being made “on behalf of” the original user.

Prerequisites

Only Custom API clients associated with a resource server can use the OBO token exchange. A Custom API client is linked to a resource server when they share the same identifier. Custom API clients have the following requirements:
  • Set app_type to resource_server.
  • Set resource_server_identifier to the valid resource server, i.e., https://my-api.example.com. Auth0 uses the resource server identifier as the audience parameter in authorization calls.
Because Custom API clients are first-party clients, make sure you skip user consent for the APIs your first-party client needs to access.

Create Custom API client

You can create a Custom API client using the Auth0 Dashboard or Management API.
To create a Custom API client in the Auth0 Dashboard:
  1. Navigate to Applications > APIs and select your backend API.
My Test OBO API
  1. Select Add Application and enter an application name.
  2. Select Add.
Once the application has been successfully created, review it by selecting Configure Application, then scroll to Application Properties. The Application Type is a Custom API Client.
My Test OBO API

Create client grant

You need to create a user-delegated client grant between the Custom API client and the downstream API to authorize access.
  1. Navigate to Applications > Applications and select your Custom API client.
  2. Under API Access, find your resource server (i.e., https://my-api.example.com) and select Edit.
  3. Under User-Delegated Access, select Grant Access, then select the permissions you want to grant or Always grant all permissions.
  4. Select Save.

Configure the OBO token exchange

Learn how to configure your Custom API client with the OBO token exchange grant.
  1. Navigate to Applications > Applications and select your Custom API client.
  2. Under Token Exchange, toggle on On-Behalf-Of Token Exchange.
  3. Select Save.

Perform OBO token exchange

To perform the OBO token exchange, you can use auth0-api-js, auth0_api_python, or the Authentication API.
Cache access tokens for the lifetime of the token instead of requesting a new token for each API call. Access tokens are reusable until they expire; repeated token exchanges waste resources, increase latency, and may trigger rate limits.
Before you begin, make sure you’ve installed the auth0-api-js library and its dependencies.First, initialize the ApiClient with your MCP server’s credentials:
import { ApiClient } from '@auth0/auth0-api-js';

const apiClient = new ApiClient({
  domain: 'YOUR_AUTH0_DOMAIN',
  audience: 'YOUR_MCP_SERVER_AUDIENCE',
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
});
Then, use the getTokenOnBehalfOf() method to exchange tokens:
const result = await apiClient.getTokenOnBehalfOf(accessToken, {
  audience: 'YOUR_DOWNSTREAM_API_AUDIENCE',
  scope: 'read:private',  // Optional
});
getTokenOnBehalfOf() returns an object containing:
  • accessToken: The new token for your downstream API
  • scope: The granted scopes
  • expiresIn: Token expiration time in seconds

Organizations support

When a user authenticates through an organization, the access token includes an org_id claim. OBO token exchange preserves this organization context throughout the delegation chain. When Auth0 receives an OBO token exchange request with an org-bound access token, it validates:
  • The org_id exists in your tenant
  • The user (identified by sub) is a member of that organization
If validation fails, Auth0 rejects the token exchange request. If successful, Auth0 issues a new access token that:
  • Contains the same org_id claim as the original token
  • Applies the same organization-specific RBAC policies
  • Makes the organization context available in the post-login Actions trigger via the event.organization property