Skip to main content

Documentation Index

Fetch the complete documentation index at: https://auth0.com/ai/docs/llms.txt

Use this file to discover all available pages before exploring further.

Auth0 implements the On-Behalf-Of (OBO) token exchange (RFC 8693) to enable MCP servers to preserve user identity and permissions when calling downstream APIs. In the OBO token exchange flow, the MCP server acts in a dual role: as a resource server when receiving requests from the client, and as a client when calling your downstream API on the user’s behalf. To call your APIs on a user’s behalf, the MCP server must exchange the Auth0 access token it receives from the MCP client for a new access token with a different audience. The original token has the MCP server as its audience, while the new token has your downstream API as its audience.
MCP Diagram
By the end of this quickstart, you will have:
  • An MCP server that calls your protected APIs on behalf of authenticated users using the OBO token exchange
  • Token verification and exchange using the @auth0/auth0-api-js library (JavaScript) or auth0-api-python library (Python)
  • A working example demonstrating the end-to-end OBO token exchange flow from client to MCP server to downstream API

Prerequisites

To continue with this quickstart, you need to have an Auth0 account.
To use the resource parameter in your access tokens, you need to enable the Resource Parameter Compatibility Profile.The quickest way to enable it is through the Auth0 Dashboard:
  1. Navigate to Settings on the left sidebar.
  2. Select Advanced in the top right corner.
  3. Scroll down to the Settings section and enable the Resource Parameter Compatibility Profile toggle.
This guide uses Auth0 CLI to configure an Auth0 tenant for secure MCP tool access.
  1. Follow the Auth0 CLI installation instructions.
  2. Log in to your account with the Auth0 CLI:
auth0 login --scopes "read:client_grants,create:client_grants,delete:client_grants,read:clients,create:clients,update:clients,read:resource_servers,create:resource_servers,update:resource_servers,read:roles,create:roles,update:roles,update:tenant_settings,read:connections,update:connections"
Confirm you are in the correct tenant by double-checking the tenant domain in the terminal, or run the command below:
auth0 tenants list
If more than one tenant is configured, the default tenant will be indicated by an arrow.
To simplify the process of interacting with the Auth0 CLI, we recommend installing jq. This will allow you to easily parse JSON responses from the CLI.
brew install jq

Configure tenant settings

To allow third-party clients like MCP Inspector to use a connection such as a username-and-password database or a social connection, you need to promote the connection to a domain-level connection. Learn more about configuring third-party applications.
1

List Your Connection

List your connections to get their IDs
auth0 api get connections
2

Choose Your Connections

From the list, identify which connections you want to use for the MCP server and copy the ID.
  • For the username-and-password database, look for the connection with the strategy auth0.
  • For the Google social connection, look for the connection with the strategy google-oauth2.
3

Upgrade the Connection to Domain-Level

For each of those specific connection IDs, run the following command to mark it as a domain-level connection. Replace YOUR_CONNECTION_ID with the actual ID (e.g., con_XXXXXXXXXXXXXXXX).
auth0 api patch connections/YOUR_CONNECTION_ID --data '{"is_domain_connection": true}'
The sample application in this quickstart is prepared to show different tools depending on the role of a given user. For example, a Tool Administrator will have access to different tools than a Tool User.Let’s create roles that allow you to control which users can access which tools.For each role you need (e.g., “Tool Administrator”, “Tool User”), run the create command as below.
# Example for an admin role
auth0 roles create --name "Tool Administrator" --description "Grants access to all MCP tools"

# Example for a basic user role
auth0 roles create --name "Tool User" --description "Grants access to basic MCP tools"
Save the IDs from the output (they start with rol_), as you’ll need them in a later step to assign permissions and users to these roles.
Find users and assign them to the roles. You can search by their email address:
auth0 users search --query "email:\"example@google.com\""
Copy the user ID (USERID) from the last command’s output, then assign the role to the user:
auth0 users roles assign "USER_ID_HERE" --roles "YOUR_ROLE_ID_HERE"

Set up the Auth0 applications and APIs

The MCP server’s dual role requires two configurations in Auth0:
  1. As a resource server: An API that the MCP client calls
  2. As a client: An application that can exchange tokens to call downstream APIs
Let’s set up both configurations.

Create an API to represent your MCP server

An MCP server is treated just like any other API in your Auth0 tenant, which means you can use the same access control, scoping, and authorization features. The API (also known as a Resource Server) represents your protected MCP Server. Run the following command to create an API in Auth0:
auth0 api post resource-servers --data '{
  "identifier": "http://localhost:3001/",
  "name": "MCP Tools API",
  "signing_alg": "RS256",
  "token_dialect": "rfc9068_profile_authz",
  "enforce_policies": true,
  "scopes": [
    {"value": "tool:whoami", "description": "Access the WhoAmI tool"},
    {"value": "tool:greet", "description": "Access the Greeting tool"}
  ]
}'
Note that rfc9068_profile_authz is used as the token dialect to include the permissions claim in the access token, which can be useful for the API to check user permissions without making additional calls. For more details on protecting APIs with Auth0, check our quickstarts.
After creating the API and roles, assign the API permissions to the roles.
# Example for admin role (all scopes)
auth0 roles permissions add YOUR_ADMIN_ROLE_ID --api-id "http://localhost:3001/" --permissions "tool:whoami,tool:greet"

# Example for user role (one scope)
auth0 roles permissions add YOUR_USER_ROLE_ID --api-id "http://localhost:3001/" --permissions "tool:whoami"
Replace YOUR_ADMIN_ROLE_ID and YOUR_USER_ROLE_ID with the role IDs you saved when creating the roles.

Create an application for your MCP server

Create an application that allows your MCP server to act as a client for the OBO token exchange. This application must:
  • Have the same identifier as your MCP server API (resource_server_identifier)
  • Use app_type: resource_server to link it to the MCP server
  • Enable OBO token exchange in the token_exchange configuration
The following command creates an application linked to your MCP server with OBO token exchange enabled:
auth0 api post clients --data '{
  "name": "MCP Server Client",
  "app_type": "resource_server",
  "oidc_conformant": true,
  "resource_server_identifier": "http://localhost:3001/",
  "token_exchange": {
    "allow_any_profile_of_type": ["on_behalf_of_token_exchange"]
  }
}' | jq -c '{client_id, client_secret}' > auth0-app-details.json
Save the client_id and client_secret from the output; you’ll need them to configure your MCP server environment.

Create a downstream API

Create the protected API that your MCP server will call on the user’s behalf. This API represents your business logic API (e.g., a calendar API, document API, etc.). The following command creates an API with the following configurations:
  • Client grants: Gives the MCP server user-delegated and client access the API
  • Scope enforcement: Requires specific permissions (read:private)
  • First-party status: Skips user consent for your own applications
auth0 api post resource-servers --data '{
  "identifier": "http://localhost:8787/",
  "name": "Protected Downstream API",
  "signing_alg": "RS256",
  "enforce_policies": true,
  "scopes": [
    {"value": "read:private", "description": "Private scope"}
  ],
  "subject_type_authorization": {
    "user": {
        "policy": "require_client_grant"
    },
    "client": {
        "policy": "require_client_grant"
    }
  },
  "skip_consent_for_verifiable_first_party_clients": false
}'
Save the API identifier (audience) from the output; you’ll configure it in your environment variables as API_AUTH0_AUDIENCE.

Sample app

The sample app demonstrates the complete OBO flow with a greet tool that:
  1. Receives an authenticated request from an MCP client
  2. Exchanges the token for downstream API access
  3. Calls your protected API on the user’s behalf
  4. Returns the result

Install packages

Make sure you have npm installed. Then, in the fastmcp-mcp-on-behalf-of-tokenexchange-js directory, install the required packages:
npm install

Create your environment file

From the root of your sample app directory, run the following command to create a new .env file populated with all the required environment variables:
CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) \
&& CLIENT_SECRET=$(jq -r '.client_secret' auth0-app-details.json) \
&& DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') \
&& touch .env \
&& echo "AUTH0_DOMAIN=${DOMAIN}" > .env \
&& echo "AUTH0_AUDIENCE=http://localhost:3001/" >> .env \
&& echo "PORT=3001" >> .env \
&& echo "MCP_SERVER_URL=http://localhost:3001/" >> .env \
&& echo "MCP_AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env \
&& echo "MCP_AUTH0_CLIENT_SECRET=${CLIENT_SECRET}" >> .env \
&& echo "MCP_AUTH0_EXCHANGE_SCOPE=read:private" >> .env \
&& echo "API_AUTH0_AUDIENCE=http://localhost:8787/" >> .env \
&& echo "API_BASE_URL=http://localhost:8787" >> .env \
&& rm auth0-app-details.json \
&& echo ".env file created with your Auth0 details:" \
&& cat .env

Run the MCP server and the API

Run this command to start your server:
npm run start
And in a separate window run this command to start the API:
npm run start:api

Exchange your access token

To call your APIs on a user’s behalf, the MCP server uses the OBO token exchange to exchange the Auth0 access token it received from the MCP client (with the audience set to the MCP server itself) for a new Auth0 access token with the audience set to your API.

Token exchange implementation

The MCP server exchanges tokens in two steps:

1. Wrapper function: bearerForUpstream()

This function safely handles the token exchange process and error handling:
async function bearerForUpstream(accessToken: string) {
  if (!accessToken) return { token: null, scopes: null };

  try {
    const result = await exchangeTokenOnBehalfOf(accessToken);
    return {
      token: result.accessToken,
      scopes: result.scope,
    }
  } catch (err) {
    console.error('Error during token exchange:', err);
    throw err;
  }
}
bearerForUpstream() calls exchangeTokenOnBehalfOf() and, upon a successful exchange, returns the new accessToken and its associated scope. If the exchange fails, it logs the error and re-throws it to be handled upstream.

2. Exchange function: exchangeTokenOnBehalfOf()

This function, located in src/auth0.ts, calls Auth0’s OBO token exchange endpoint using the SDK.First, initialize the ApiClient with the credentials of the MCP server application:
const apiClient = new ApiClient({
  domain: AUTH0_DOMAIN,
  audience: AUTH0_AUDIENCE,
  clientId: MCP_AUTH0_CLIENT_ID,
  clientSecret: MCP_AUTH0_CLIENT_SECRET,
});
Once you’ve configured the MCP server application, the exchangeTokenOnBehalfOf() function uses the client’s getTokenOnBehalfOf() method to perform the token exchange.getTokenOnBehalfOf() implements the OBO token exchange flow, which allows the MCP server to obtain a new token to call the downstream API while preserving the user’s identity. It takes in the accessToken, audience, and scope parameters.
export async function exchangeTokenOnBehalfOf(accessToken: string) {
    return await apiClient.getTokenOnBehalfOf(accessToken, {
      audience: API_AUTH0_AUDIENCE,
      ...(MCP_AUTH0_EXCHANGE_SCOPE && { scope: MCP_AUTH0_EXCHANGE_SCOPE }),
    });
}

Testing your MCP server

Now that your MCP server is up and running, you can test it by using tools like MCP Inspector.

Learn how to test your Auth0-powered MCP server

Next steps