From 7643dde8b4e66a2bd7a4e07888c78497db57f9e6 Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Mon, 27 Apr 2026 14:29:22 -0700 Subject: [PATCH 1/7] feat: update MCP quickstart for On-Behalf-Of Token Exchange Updates the "Call Your API on a User's Behalf" quickstart to reflect the new On-Behalf-Of (OBO) Token Exchange implementation. JavaScript changes (PR #79): - Update sample folder: fastmcp-mcp-on-behalf-of-tokenexchange-js - Replace Custom Token Exchange with On-Behalf-Of terminology - Simplify environment variables (remove MCP_AUTH0_SUBJECT_TOKEN_TYPE) - Update token exchange: exchangeTokenOnBehalfOf() using getTokenOnBehalfOf() - Simplify MCP_AUTH0_EXCHANGE_SCOPE to "read:private" Python changes (PR #81): - Update sample folder: fastmcp-mcp-on-behalf-of-tokenexchange-python - Replace Custom Token Exchange with On-Behalf-Of terminology - Update token exchange: exchange_token_on_behalf_of() using get_token_on_behalf_of() Additional improvements: - Add rate limit note for Auth0 for AI Agents add-on - Add client grant configuration instructions - Add OBO token exchange enablement component - Update prerequisites with OBO-specific requirements Co-Authored-By: Claude Opus 4.7 --- .../call-your-apis-on-users-behalf.mdx | 52 ++++++++++--------- .../call-your-apis/create-env-file.mdx | 5 +- .../exchange-access-token-js.mdx | 32 ++++++------ .../exchange-access-token-python.mdx | 29 ++++++----- .../get-started/enable-obo-token-exchange.mdx | 5 ++ .../pre-reqs/enable-obo-token-exchange.mdx | 5 ++ .../get-started/pre-reqs/prerequisites.mdx | 13 +++-- 7 files changed, 77 insertions(+), 64 deletions(-) create mode 100644 auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx create mode 100644 auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx diff --git a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx index 3063b45391..42bf3e0b73 100644 --- a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx +++ b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx @@ -11,6 +11,7 @@ permalink: "/mcp/get-started/call-your-apis-on-users-behalf" --- import MCPGetStartedPrerequisites from "/snippets/mcp/get-started/pre-reqs/prerequisites.mdx"; +import MCPGetStartedEnableOBOTokenExchangeStep from "/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx"; import MCPGetStartedConfigTenantSettings from "/snippets/mcp/get-started/config-tenant/config-tenant-settings.mdx"; import MCPGetStartedCreateRoles from "/snippets/mcp/get-started/config-tenant/roles-management.mdx"; import CreateProfile from "/snippets/mcp/get-started/create-custom-token-exchange-profile.mdx"; @@ -25,9 +26,13 @@ import RunMcpServerPython from "/snippets/mcp/get-started/call-your-apis/run-mcp import ExchangeAccessTokenJs from "/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx"; import ExchangeAccessTokenPython from "/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx"; + +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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. + + To call your APIs on behalf of your users, the MCP server needs 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. -In Auth0, this is called Custom Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). This flow involves the MCP server acting as both a resource server (for the client) and a client (for the upstream API). +In Auth0, this is called On-Behalf-Of Token Exchange (OBO) and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). This flow involves the MCP server acting as both a resource server (for the client) and a client (for the upstream API). @@ -59,7 +64,7 @@ By the end of this quickstart, you should have an MCP server that can: ## Set up the Auth0 Applications and APIs -When an MCP server needs to call underlying APIs, it needs to perform a Custom Token Exchange to obtain an access token with the audience set to the API rather than the MCP server itself. Because of this architecture, the MCP server acts in a dual role. +When an MCP server needs to call underlying APIs, it needs to perform an On-Behalf-Of Token Exchange to obtain an access token with the audience set to the API rather than the MCP server itself. Because of this architecture, the MCP server acts in a dual role. 1. To the MCP client (e.g., an IDE, an AI assistant), the MCP server acts as a **Resource Server** (an API). 2. To the underlying API (e.g., your own API), the MCP server acts as a **Client**. @@ -74,7 +79,7 @@ Because of this, you will set up the MCP server twice on your Auth0 tenant, both ### Create an Application for your MCP server -In the custom token exchange scenario, the MCP server acts as a client in order to obtain an Auth0 access token with custom token exchange: +The MCP server acts as a client in order to obtain an Auth0 access token with On-Behalf-Of token exchange: ```shell wrap lines auth0 api post clients --data '{ @@ -108,7 +113,16 @@ auth0 api post resource-servers --data '{ }' | jq -r '"Audience: " + .identifier' ``` -Save the `Audience` from the command output; you'll need it in a later step. +Save the `Audience` from the command output; you'll need it in a later step. Make sure you enable **Allow Skipping User Consent** for the API in the Auth0 Dashboard. + +### Create client grant + +You need to create a [user-delegated client grant](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) 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 **APIs**, find your resource server (i.e., `https://my-api.example.com`) and select **Edit**. +3. Under **User-Delegated Access**, select **Authorized**, then select **Specific Permissions** with the permissions you want to grant or **All**. +4. Select **Save**. ## Sample app @@ -120,7 +134,7 @@ Save the `Audience` from the command output; you'll need it in a later step. Once downloaded, extract the files and open the project in your preferred IDE. @@ -131,18 +145,18 @@ Save the `Audience` from the command output; you'll need it in a later step. ```shell wrap lines git clone https://github.com/auth0-samples/auth0-ai-samples.git - cd auth0-ai-samples/auth-for-mcp/fastmcp-mcp-customtokenexchange-js + cd auth0-ai-samples/auth-for-mcp/fastmcp-mcp-on-behalf-of-tokenexchange-js ``` Once cloned, open the project in your preferred IDE. - The sample app demonstrates custom token exchange with a `greet` tool that calls your protected API on behalf of the authenticated user. + The sample app demonstrates on-behalf-of token exchange with a `greet` tool that calls your protected API on behalf of the authenticated user. ## Install packages - Ensure you have npm installed or follow the instructions to [install npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) in its documentation. In the `fastmcp-mcp-customtokenexchange-js` directory, install the required packages: + Ensure you have npm installed or follow the instructions to [install npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) in its documentation. In the `fastmcp-mcp-on-behalf-of-tokenexchange-js` directory, install the required packages: ```shell npm install @@ -151,12 +165,6 @@ Save the `Audience` from the command output; you'll need it in a later step. ## Create your environment file - ## Use Custom Token Exchange Action - - - ## Set up the token exchange profile - - ## Run the MCP server and the API @@ -171,7 +179,7 @@ Save the `Audience` from the command output; you'll need it in a later step. Once downloaded, extract the files and open the project in your preferred IDE. @@ -181,18 +189,18 @@ Save the `Audience` from the command output; you'll need it in a later step. ```shell wrap lines git clone https://github.com/auth0-samples/auth0-ai-samples.git - cd auth0-ai-samples/auth-for-mcp/fastmcp-mcp-customtokenexchange-python + cd auth0-ai-samples/auth-for-mcp/fastmcp-mcp-on-behalf-of-tokenexchange-python ``` Once cloned, open the project in your preferred IDE. - The sample app demonstrates custom token exchange with a `greet` tool that calls your protected API on behalf of the authenticated user. + The sample app demonstrates on-behalf-of token exchange with a `greet` tool that calls your protected API on behalf of the authenticated user. ## Install packages - Ensure you have poetry installed or follow the instructions to [install poetry](https://python-poetry.org/docs/) in its documentation. In the `fastmcp-mcp-customtokenexchange-python` directory, install the required packages: + Ensure you have poetry installed or follow the instructions to [install poetry](https://python-poetry.org/docs/) in its documentation. In the `fastmcp-mcp-on-behalf-of-tokenexchange-python` directory, install the required packages: ```shell poetry install @@ -201,12 +209,6 @@ Save the `Audience` from the command output; you'll need it in a later step. ## Create your environment file - ## Use Custom Token Exchange Action - - - ## Set up the token exchange profile - - ## Run the MCP server and the API diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/create-env-file.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/create-env-file.mdx index 890c04dd78..a888f34cd7 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/create-env-file.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/create-env-file.mdx @@ -11,10 +11,9 @@ CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) \ && 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_SUBJECT_TOKEN_TYPE=urn:fastmcp:mcp" >> .env \ -&& echo "MCP_AUTH0_EXCHANGE_SCOPE=openid offline_access read:private" >> .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 \ +&& echo "API_BASE_URL=http://localhost:8787" >> .env \ && rm auth0-app-details.json \ && echo ".env file created with your Auth0 details:" \ && cat .env diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx index 901ea51677..2efcbc6b2d 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx @@ -1,17 +1,17 @@ -To call your APIs on behalf of your users, the MCP server needs 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. In Auth0, this is called Custom Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). +To call your APIs on behalf of your users, the MCP server needs 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. In Auth0, this is called On-Behalf-Of Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). ### The Orchestrator: `bearerForUpstream` -The process begins with the `bearerForUpstream` function. Its main job is to take the initial token (the `subjectToken`), manage the exchange process, and handle any potential errors gracefully. +The process begins with the `bearerForUpstream` function. Its main job is to take the initial access token, manage the exchange process, and handle any potential errors gracefully. This function serves as a safe wrapper around our exchange logic. ```javascript wrap lines highlight={5} -async function bearerForUpstream(subjectToken: string) { - if (!subjectToken) return { token: null, scopes: null }; +async function bearerForUpstream(accessToken: string) { + if (!accessToken) return { token: null, scopes: null }; try { - const result = await exchangeCustomToken(subjectToken); + const result = await exchangeTokenOnBehalfOf(accessToken); return { token: result.accessToken, scopes: result.scope, @@ -23,30 +23,32 @@ async function bearerForUpstream(subjectToken: string) { } ``` -As you can see, it calls `exchangeCustomToken` and, on 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. +As you can see, it calls `exchangeTokenOnBehalfOf` and, on 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. -### The core logic: `exchangeCustomToken` +### The core logic: `exchangeTokenOnBehalfOf` -This function, located in `src/auth0.ts`, contains the actual token exchange logic. It uses the `ApiClient` from the `auth0-api-js` SDK to simplify the interaction with Auth0's `/oauth/token` endpoint. +This function, located in `src/auth0.ts`, contains the actual token exchange logic. It uses the `ApiClient` from the `@auth0/auth0-api-js` SDK to simplify the interaction with Auth0's `/oauth/token` endpoint. -First, we initialize the `ApiClient` with the credentials of the application performing the exchange: +First, we initialize the `ApiClient` with the credentials of the MCP server application: ```javascript wrap lines - const exchangeClient = new ApiClient({ +const apiClient = new ApiClient({ domain: AUTH0_DOMAIN, - audience: API_AUTH0_AUDIENCE, + audience: AUTH0_AUDIENCE, clientId: MCP_AUTH0_CLIENT_ID, clientSecret: MCP_AUTH0_CLIENT_SECRET, }); ``` -With the client configured, the `exchangeCustomToken` function uses the client's `getTokenByExchangeProfile` method to perform the token exchange. This method implements the [Custom Token Exchange](https://auth0.com/docs/authenticate/custom-token-exchange) flow. + +With the client configured, the `exchangeTokenOnBehalfOf` function uses the client's `getTokenOnBehalfOf` method to perform the token exchange. This method implements the On-Behalf-Of Token Exchange flow, which allows the MCP server to obtain a new token for calling the downstream API while preserving the user's identity. ```javascript wrap lines -export async function exchangeCustomToken(subjectToken: string) { - return await exchangeClient.getTokenByExchangeProfile(subjectToken, { - subjectTokenType: MCP_AUTH0_SUBJECT_TOKEN_TYPE, +export async function exchangeTokenOnBehalfOf(accessToken: string) { + return await apiClient.getTokenOnBehalfOf(accessToken, { audience: API_AUTH0_AUDIENCE, ...(MCP_AUTH0_EXCHANGE_SCOPE && { scope: MCP_AUTH0_EXCHANGE_SCOPE }), }); } ``` + +The key difference from custom token exchange is that this uses the `getTokenOnBehalfOf` method with just the `audience` and optional `scope` parameters, making it simpler and more straightforward for the on-behalf-of use case. diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx index 3a8b14b057..470869a04a 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx @@ -1,9 +1,9 @@ -To call your APIs on behalf of your users, the MCP server needs 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. In Auth0, this is called Custom Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). +To call your APIs on behalf of your users, the MCP server needs 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. In Auth0, this is called On-Behalf-Of Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). ### How tools use token exchange Here's how the `greet` tool performs token exchange and calls the upstream API: -```python wrap lines highlight={10,11,12,13,14,19} +```python wrap lines highlight={10,11,12,13,14,17} @mcp.tool(name="greet") @require_scopes(["tool:greet"]) async def greet(name: str, ctx: Context) -> str: @@ -14,7 +14,7 @@ async def greet(name: str, ctx: Context) -> str: logger.info(f"Greet tool invoked for user: {user_id}") # Exchange token and call upstream API - exchange_result = await exchange_custom_token( + exchange_result = await exchange_token_on_behalf_of( ctx.request_context.request.state.api_client, auth_info["token"] ) @@ -29,32 +29,33 @@ async def greet(name: str, ctx: Context) -> str: return f"Hello, {user_name} ({user_id})!\nUpstream API Response: {json.dumps(upstream_result, indent=2)}" ``` -### The core logic: `exchange_custom_token` +### The core logic: `exchange_token_on_behalf_of` -The Python implementation uses the `exchange_custom_token` function that handles the token exchange process. +The Python implementation uses the `exchange_token_on_behalf_of` function that handles the token exchange process. -```python wrap lines highlight={3,4,5,6,7,8} -async def exchange_custom_token(api_client, subject_token: str) -> dict: - """Exchange subject token for access token via Custom Token Exchange.""" - result = await api_client.get_token_by_exchange_profile( - subject_token=subject_token, - subject_token_type=config.mcp_auth0_subject_token_type, +```python wrap lines highlight={3,4,5,6} +async def exchange_token_on_behalf_of(api_client, access_token: str) -> dict: + """Exchange access token for downstream API token via On-Behalf-Of Token Exchange.""" + result = await api_client.get_token_on_behalf_of( + access_token=access_token, audience=config.api_auth0_audience, scope=config.mcp_auth0_exchange_scope or None ) return {"token": result["access_token"], "scopes": result.get("scope", "")} ``` -This function uses the `get_token_by_exchange_profile` method of `ApiClient` from the `auth0-api-python` SDK and, on a successful exchange, returns the new access token and its associated scopes. This method implements the [Custom Token Exchange](https://auth0.com/docs/authenticate/custom-token-exchange) flow. +This function uses the `get_token_on_behalf_of` method of `ApiClient` from the `auth0-api-python` SDK and, on a successful exchange, returns the new access token and its associated scopes. This method implements the On-Behalf-Of Token Exchange flow, which allows the MCP server to obtain a new token for calling the downstream API while preserving the user's identity. + +The key difference from custom token exchange is that this uses the simpler `get_token_on_behalf_of` method with just the `access_token`, `audience`, and optional `scope` parameters, making it more straightforward for the on-behalf-of use case. ### Client Configuration -The `ApiClient` is initialized in the `Auth0Middleware` (located in `src/auth0/middleware.py`) with the credentials of the application performing the exchange: +The `ApiClient` is initialized in the `Auth0Middleware` (located in `src/auth0/middleware.py`) with the credentials of the MCP server application: ```python src/auth0/middleware.py wrap lines self.client = ApiClient(ApiClientOptions( domain=domain, # AUTH0_DOMAIN env var - audience=audience, # API_AUTH0_AUDIENCE env var + audience=audience, # AUTH0_AUDIENCE env var client_id=client_id, # MCP_AUTH0_CLIENT_ID env var client_secret=client_secret # MCP_AUTH0_CLIENT_SECRET env var )) diff --git a/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx b/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx new file mode 100644 index 0000000000..cd052c5f72 --- /dev/null +++ b/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx @@ -0,0 +1,5 @@ +To enable your tenant to use the On-Behalf-Of Token Exchange: + +1. Navigate to **Applications > Applications** and select your MCP client. Only Custom API clients associated with a resource server can use the OBO token exchange. +2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. +3. Select **Save**. diff --git a/auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx b/auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx new file mode 100644 index 0000000000..4bc6700025 --- /dev/null +++ b/auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx @@ -0,0 +1,5 @@ +Enable your tenant to use the On-Behalf-Of Token Exchange: + +1. Navigate to **Applications > Applications** and select your MCP client. Only Custom API clients associated with a resource server can use the OBO token exchange. +2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. +3. Select **Save**. diff --git a/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx b/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx index 1c601f430e..bdf566bcc0 100644 --- a/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx +++ b/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx @@ -3,21 +3,20 @@ import MCPGetStartedAuth0CLIStep from "/snippets/mcp/get-started/pre-reqs/auth0- ## Prerequisites - -Auth for MCP is currently available in Early Access. To join the Early Access program, please complete [this form](https://forms.gle/hvJ1ZRLmHr9YjV2a9). We'll reach out to you when your request is processed. - - To continue with this quickstart, you need to have an [Auth0 account](https://auth0.com/signup). - + + + + - + - + To simplify the process of interacting with the Auth0 CLI, we recommend installing [jq](https://jqlang.org/download/). This will allow you to easily parse JSON responses from the CLI. From 10583a02ff610a806318239c9259fd63f2c5962d Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Mon, 27 Apr 2026 19:11:36 -0700 Subject: [PATCH 2/7] Edits --- .../call-your-apis-on-users-behalf.mdx | 51 +++++++++++-------- .../exchange-access-token-js.mdx | 22 ++++---- .../exchange-access-token-python.mdx | 10 ++-- .../get-started/enable-obo-token-exchange.mdx | 4 +- .../pre-reqs/enable-obo-token-exchange.mdx | 5 -- .../get-started/pre-reqs/prerequisites.mdx | 9 ++-- 6 files changed, 48 insertions(+), 53 deletions(-) delete mode 100644 auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx diff --git a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx index 42bf3e0b73..2c96ad5786 100644 --- a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx +++ b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx @@ -11,7 +11,6 @@ permalink: "/mcp/get-started/call-your-apis-on-users-behalf" --- import MCPGetStartedPrerequisites from "/snippets/mcp/get-started/pre-reqs/prerequisites.mdx"; -import MCPGetStartedEnableOBOTokenExchangeStep from "/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx"; import MCPGetStartedConfigTenantSettings from "/snippets/mcp/get-started/config-tenant/config-tenant-settings.mdx"; import MCPGetStartedCreateRoles from "/snippets/mcp/get-started/config-tenant/roles-management.mdx"; import CreateProfile from "/snippets/mcp/get-started/create-custom-token-exchange-profile.mdx"; @@ -30,9 +29,9 @@ import ExchangeAccessTokenPython from "/snippets/mcp/get-started/call-your-apis/ 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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. -To call your APIs on behalf of your users, the MCP server needs 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. +Auth0 implements the On-Behalf-Of (OBO) token exchange ([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html)) to enable MCP servers to preserve user identity and permissions when calling downstream APIs. In this 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 upstream API on the user's behalf. -In Auth0, this is called On-Behalf-Of Token Exchange (OBO) and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). This flow involves the MCP server acting as both a resource server (for the client) and a client (for the upstream API). +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. @@ -62,14 +61,14 @@ By the end of this quickstart, you should have an MCP server that can: -## Set up the Auth0 Applications and APIs +## Set up the Auth0 applications and APIs -When an MCP server needs to call underlying APIs, it needs to perform an On-Behalf-Of Token Exchange to obtain an access token with the audience set to the API rather than the MCP server itself. Because of this architecture, the MCP server acts in a dual role. +To call downstream APIs, the MCP server uses the OBO token exchange to obtain an access token with the audience set to the API rather than the MCP server itself. The MCP server acts in a dual role: -1. To the MCP client (e.g., an IDE, an AI assistant), the MCP server acts as a **Resource Server** (an API). -2. To the underlying API (e.g., your own API), the MCP server acts as a **Client**. +1. To the MCP client (e.g., an IDE, an AI assistant), the MCP server acts as a resource server (an API). +2. To the underlying API (e.g., your own API), the MCP server acts as a client. -Because of this, you will set up the MCP server twice on your Auth0 tenant, both as an API and as a Client. +As a result, you need to set up the MCP server twice on your Auth0 tenant, both as an API and as a Client. ### Create an API to represent your MCP server @@ -77,9 +76,11 @@ Because of this, you will set up the MCP server twice on your Auth0 tenant, both -### Create an Application for your MCP server +### Create an application for your MCP server -The MCP server acts as a client in order to obtain an Auth0 access token with On-Behalf-Of token exchange: +The MCP server acts as a client to obtain an Auth0 access token with the OBO token exchange. + +The following code sample creates an application linked to your MCP server by sharing the same identifier with an `app_type` set to `resource_server`: ```shell wrap lines auth0 api post clients --data '{ @@ -93,13 +94,19 @@ auth0 api post clients --data '{ }' | jq -c '{client_id, client_secret}' > auth0-app-details.json ``` -The output of the command will be a JSON object with the `client_id` and `client_secret` of the newly created client, which will be used in the next steps to configure the MCP server environment. +The output of the command will be a JSON object with the `client_id` and `client_secret` of the newly created MCP server application, which will be used in the next steps to configure the MCP server environment. + +### Configure your application with the OBO token exchange + +Configure your application with the OBO token exchange grant using the Auth0 Dashboard: -And finally, because you need an API to call to, let's create one. +1. Navigate to **Applications > Applications** and select your MCP server application. +2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. +3. Select **Save**. ### Create a first-party API to call from the MCP server -Since your objective is to have the MCP server make a tool call that calls a protected first-party API you need to configure an API in Auth0, this will be your upstream API: +Since your objective is to have the MCP server make a tool call that calls a protected first-party API, configure an API in Auth0: ```shell auth0 api post resource-servers --data '{ @@ -117,10 +124,10 @@ Save the `Audience` from the command output; you'll need it in a later step. Mak ### Create client grant -You need to create a [user-delegated client grant](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) between the Custom API client and the downstream API to authorize access. +You need to create a [user-delegated client grant](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) between your MCP server application and the downstream API to authorize access. -1. Navigate to **Applications > Applications** and select your Custom API client. -2. Under **APIs**, find your resource server (i.e., `https://my-api.example.com`) and select **Edit**. +1. Navigate to **Applications > Applications** and select your MCP server application. +2. Under **APIs**, find your resource server (i.e., `http://localhost:8787/`) and select **Edit**. 3. Under **User-Delegated Access**, select **Authorized**, then select **Specific Permissions** with the permissions you want to grant or **All**. 4. Select **Save**. @@ -140,7 +147,7 @@ You need to create a [user-delegated client grant](https://auth0.com/docs/get-st Once downloaded, extract the files and open the project in your preferred IDE. - Clone the repository and navigate to the sample app folder which includes a FastMCP MCP server with an Auth0 integration and a protected API built with + Clone the repository and navigate to the sample app folder, which includes a FastMCP MCP server with an Auth0 integration and a protected API built with [Fastify](https://fastify.dev/). ```shell wrap lines @@ -152,11 +159,11 @@ You need to create a [user-delegated client grant](https://auth0.com/docs/get-st - The sample app demonstrates on-behalf-of token exchange with a `greet` tool that calls your protected API on behalf of the authenticated user. + The sample app uses the OBO token exchange with a `greet` tool that calls your protected API on the authenticated user's behalf. ## Install packages - Ensure you have npm installed or follow the instructions to [install npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) in its documentation. In the `fastmcp-mcp-on-behalf-of-tokenexchange-js` directory, install the required packages: + Make sure you have [npm installed](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Then, in the `fastmcp-mcp-on-behalf-of-tokenexchange-js` directory, install the required packages: ```shell npm install @@ -196,11 +203,11 @@ You need to create a [user-delegated client grant](https://auth0.com/docs/get-st - The sample app demonstrates on-behalf-of token exchange with a `greet` tool that calls your protected API on behalf of the authenticated user. + The sample app uses the OBO token exchange with a `greet` tool that calls your protected API on the authenticated user's behalf. ## Install packages - Ensure you have poetry installed or follow the instructions to [install poetry](https://python-poetry.org/docs/) in its documentation. In the `fastmcp-mcp-on-behalf-of-tokenexchange-python` directory, install the required packages: + Make sure you have [poetry installed](https://python-poetry.org/docs/). Then, in the `fastmcp-mcp-on-behalf-of-tokenexchange-python` directory, install the required packages: ```shell poetry install diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx index 2efcbc6b2d..0f0ae05ff4 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx @@ -1,10 +1,8 @@ -To call your APIs on behalf of your users, the MCP server needs 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. In Auth0, this is called On-Behalf-Of Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). +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. -### The Orchestrator: `bearerForUpstream` +### The Orchestrator: `bearerForUpstream()` -The process begins with the `bearerForUpstream` function. Its main job is to take the initial access token, manage the exchange process, and handle any potential errors gracefully. - -This function serves as a safe wrapper around our exchange logic. +The process begins with the `bearerForUpstream()` function. It serves as a safe wrapper around our exchange logic by taking in the initial access token, managing the exchange process, and handling any potential errors: ```javascript wrap lines highlight={5} async function bearerForUpstream(accessToken: string) { @@ -23,13 +21,13 @@ async function bearerForUpstream(accessToken: string) { } ``` -As you can see, it calls `exchangeTokenOnBehalfOf` and, on 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. +`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. -### The core logic: `exchangeTokenOnBehalfOf` +### The core logic: `exchangeTokenOnBehalfOf()` -This function, located in `src/auth0.ts`, contains the actual token exchange logic. It uses the `ApiClient` from the `@auth0/auth0-api-js` SDK to simplify the interaction with Auth0's `/oauth/token` endpoint. +`exchangeTokenOnBehalfOf()`, located in `src/auth0.ts`, contains the actual token exchange logic. It uses the `ApiClient` from the `@auth0/auth0-api-js` SDK to simplify the interaction with Auth0's `/oauth/token` endpoint. -First, we initialize the `ApiClient` with the credentials of the MCP server application: +First, initialize the `ApiClient` with the credentials of the MCP server application: ```javascript wrap lines const apiClient = new ApiClient({ @@ -40,7 +38,9 @@ const apiClient = new ApiClient({ }); ``` -With the client configured, the `exchangeTokenOnBehalfOf` function uses the client's `getTokenOnBehalfOf` method to perform the token exchange. This method implements the On-Behalf-Of Token Exchange flow, which allows the MCP server to obtain a new token for calling the downstream API while preserving the user's identity. +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. ```javascript wrap lines export async function exchangeTokenOnBehalfOf(accessToken: string) { @@ -50,5 +50,3 @@ export async function exchangeTokenOnBehalfOf(accessToken: string) { }); } ``` - -The key difference from custom token exchange is that this uses the `getTokenOnBehalfOf` method with just the `audience` and optional `scope` parameters, making it simpler and more straightforward for the on-behalf-of use case. diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx index 470869a04a..e146fe0dad 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx @@ -1,4 +1,4 @@ -To call your APIs on behalf of your users, the MCP server needs 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. In Auth0, this is called On-Behalf-Of Token Exchange and uses [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). +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. ### How tools use token exchange Here's how the `greet` tool performs token exchange and calls the upstream API: @@ -29,9 +29,9 @@ async def greet(name: str, ctx: Context) -> str: return f"Hello, {user_name} ({user_id})!\nUpstream API Response: {json.dumps(upstream_result, indent=2)}" ``` -### The core logic: `exchange_token_on_behalf_of` +### The core logic: `exchange_token_on_behalf_of()` -The Python implementation uses the `exchange_token_on_behalf_of` function that handles the token exchange process. +The Python implementation uses the `exchange_token_on_behalf_of()` function that handles the token exchange: ```python wrap lines highlight={3,4,5,6} async def exchange_token_on_behalf_of(api_client, access_token: str) -> dict: @@ -44,9 +44,7 @@ async def exchange_token_on_behalf_of(api_client, access_token: str) -> dict: return {"token": result["access_token"], "scopes": result.get("scope", "")} ``` -This function uses the `get_token_on_behalf_of` method of `ApiClient` from the `auth0-api-python` SDK and, on a successful exchange, returns the new access token and its associated scopes. This method implements the On-Behalf-Of Token Exchange flow, which allows the MCP server to obtain a new token for calling the downstream API while preserving the user's identity. - -The key difference from custom token exchange is that this uses the simpler `get_token_on_behalf_of` method with just the `access_token`, `audience`, and optional `scope` parameters, making it more straightforward for the on-behalf-of use case. +`exchange_token_on_behalf_of()` uses the `get_token_on_behalf_of()` method of `ApiClient` from the `auth0-api-python` SDK. It takes in the `access_token`, `audience`, and optional `scope` parameters, and upon a successful exchange, `returns the new access token and its associated scopes. ### Client Configuration diff --git a/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx b/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx index cd052c5f72..be5e5ffdd3 100644 --- a/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx +++ b/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx @@ -1,5 +1,5 @@ -To enable your tenant to use the On-Behalf-Of Token Exchange: +To enable your tenant to use the OBO token exchange: -1. Navigate to **Applications > Applications** and select your MCP client. Only Custom API clients associated with a resource server can use the OBO token exchange. +1. Navigate to **Applications > Applications** and select your MCP client. Only Custom API clients, or clients associated with a resource server, can use the OBO token exchange. 2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. 3. Select **Save**. diff --git a/auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx b/auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx deleted file mode 100644 index 4bc6700025..0000000000 --- a/auth4genai/snippets/mcp/get-started/pre-reqs/enable-obo-token-exchange.mdx +++ /dev/null @@ -1,5 +0,0 @@ -Enable your tenant to use the On-Behalf-Of Token Exchange: - -1. Navigate to **Applications > Applications** and select your MCP client. Only Custom API clients associated with a resource server can use the OBO token exchange. -2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. -3. Select **Save**. diff --git a/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx b/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx index bdf566bcc0..15ea911ffa 100644 --- a/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx +++ b/auth4genai/snippets/mcp/get-started/pre-reqs/prerequisites.mdx @@ -7,16 +7,13 @@ import MCPGetStartedAuth0CLIStep from "/snippets/mcp/get-started/pre-reqs/auth0- To continue with this quickstart, you need to have an [Auth0 account](https://auth0.com/signup). - - - - + - + - + To simplify the process of interacting with the Auth0 CLI, we recommend installing [jq](https://jqlang.org/download/). This will allow you to easily parse JSON responses from the CLI. From 943e929d8ffb96199a268d98ea03c3db8622a0c2 Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Mon, 27 Apr 2026 19:14:45 -0700 Subject: [PATCH 3/7] Removed OBO token exchange prereq --- .../snippets/mcp/get-started/enable-obo-token-exchange.mdx | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx diff --git a/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx b/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx deleted file mode 100644 index be5e5ffdd3..0000000000 --- a/auth4genai/snippets/mcp/get-started/enable-obo-token-exchange.mdx +++ /dev/null @@ -1,5 +0,0 @@ -To enable your tenant to use the OBO token exchange: - -1. Navigate to **Applications > Applications** and select your MCP client. Only Custom API clients, or clients associated with a resource server, can use the OBO token exchange. -2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. -3. Select **Save**. From e569cc856b512c50b76d44b651753b0f1fc38f9b Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Tue, 28 Apr 2026 14:14:10 -0700 Subject: [PATCH 4/7] Addressed Patrick's feedback --- .../call-your-apis-on-users-behalf.mdx | 52 +++++++++---------- .../pre-reqs/enable-resource-param-step.mdx | 6 +-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx index 2c96ad5786..86d137f42d 100644 --- a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx +++ b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx @@ -48,8 +48,8 @@ To call your APIs on a user's behalf, the MCP server must exchange the Auth0 acc By the end of this quickstart, you should have an MCP server that can: -* Exchange an Auth0 access token for a new access token with a different audience using the OBO token exchange -* Verify the access token using the `@auth0/auth0-api-js` library (which uses `jose` under the hood) or the `auth0-api-python` library for Python against your tenant JWKS, enforcing issuer, audience, and RS256 +* Exchange an Auth0 access token for a new access token with a different audience using the OBO token exchange. +* Verify the access token using the `@auth0/auth0-api-js` library (which uses `jose` under the hood) or the `auth0-api-python` library for Python against your tenant JWKS, enforcing issuer, audience, and RS256. @@ -68,7 +68,7 @@ To call downstream APIs, the MCP server uses the OBO token exchange to obtain an 1. To the MCP client (e.g., an IDE, an AI assistant), the MCP server acts as a resource server (an API). 2. To the underlying API (e.g., your own API), the MCP server acts as a client. -As a result, you need to set up the MCP server twice on your Auth0 tenant, both as an API and as a Client. +As a result, you need to set up the MCP server twice on your Auth0 tenant, both as an API and as a client. ### Create an API to represent your MCP server @@ -78,9 +78,9 @@ As a result, you need to set up the MCP server twice on your Auth0 tenant, both ### Create an application for your MCP server -The MCP server acts as a client to obtain an Auth0 access token with the OBO token exchange. +To enable your MCP server to act as a client to obtain an Auth0 access token with the OBO token exchange, you need to create an application linked to your MCP server. The application needs to have the same identifier as your MCP server and an `app_type` set to `resource_server`. -The following code sample creates an application linked to your MCP server by sharing the same identifier with an `app_type` set to `resource_server`: +The following code sample creates an application linked to your MCP server with the OBO token exchange grant enabled: ```shell wrap lines auth0 api post clients --data '{ @@ -89,24 +89,20 @@ auth0 api post clients --data '{ "oidc_conformant": true, "resource_server_identifier": "http://localhost:3001/", "token_exchange": { - "allow_any_profile_of_type": ["custom_authentication"] + "allow_any_profile_of_type": ["on_behalf_of_token_exchange"] } }' | jq -c '{client_id, client_secret}' > auth0-app-details.json ``` -The output of the command will be a JSON object with the `client_id` and `client_secret` of the newly created MCP server application, which will be used in the next steps to configure the MCP server environment. - -### Configure your application with the OBO token exchange - -Configure your application with the OBO token exchange grant using the Auth0 Dashboard: - -1. Navigate to **Applications > Applications** and select your MCP server application. -2. Under **Token Exchange**, toggle on **On-Behalf-Of Token Exchange**. -3. Select **Save**. +If successful, Auth0 returns a JSON object with the `client_id` and `client_secret` of the newly-created MCP server application. These credentials will be used to configure the MCP server environment. ### Create a first-party API to call from the MCP server -Since your objective is to have the MCP server make a tool call that calls a protected first-party API, configure an API in Auth0: +Create a protected first-party API that your MCP server tools can call on an authenticated user's behalf. + +The following code sample configures a protected first-party API with the following characteristics: +- Enables [skipping user consent](https://auth0.com/docs/get-started/applications/third-party-applications/user-consent-and-third-party-applications#skip-consent-for-first-party-applications) +- Creates [client grants](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) for client and user-delegated access, enabling the MCP server application to call the API on both its own and a user's behalf ```shell auth0 api post resource-servers --data '{ @@ -116,20 +112,20 @@ auth0 api post resource-servers --data '{ "enforce_policies": true, "scopes": [ {"value": "read:private", "description": "Private scope"} - ] -}' | jq -r '"Audience: " + .identifier' + ], + "subject_type_authorization": { + "user": { + "policy": "require_client_grant" + }, + "client": { + "policy": "require_client_grant" + } + }, + "skip_consent_for_verifiable_first_party_clients": false +}' ``` -Save the `Audience` from the command output; you'll need it in a later step. Make sure you enable **Allow Skipping User Consent** for the API in the Auth0 Dashboard. - -### Create client grant - -You need to create a [user-delegated client grant](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) between your MCP server application and the downstream API to authorize access. - -1. Navigate to **Applications > Applications** and select your MCP server application. -2. Under **APIs**, find your resource server (i.e., `http://localhost:8787/`) and select **Edit**. -3. Under **User-Delegated Access**, select **Authorized**, then select **Specific Permissions** with the permissions you want to grant or **All**. -4. Select **Save**. +Save the `Audience` from the command output; you'll need it in a later step. ## Sample app diff --git a/auth4genai/snippets/mcp/get-started/pre-reqs/enable-resource-param-step.mdx b/auth4genai/snippets/mcp/get-started/pre-reqs/enable-resource-param-step.mdx index 30592d82e0..804e4d47a5 100644 --- a/auth4genai/snippets/mcp/get-started/pre-reqs/enable-resource-param-step.mdx +++ b/auth4genai/snippets/mcp/get-started/pre-reqs/enable-resource-param-step.mdx @@ -1,7 +1,7 @@ -To use the `resource` parameter in your access tokens, you need to enable the compatibility profile. +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](https://manage.auth0.com/dashboard/): 1. Navigate to **Settings** on the left sidebar. -2. Click on [**Advanced**](https://manage.auth0.com/dashboard/#/tenant/advanced) in the top right corner. -3. Scroll down to the Settings section, find and enable the **Resource Parameter Compatibility Profile** toggle. \ No newline at end of file +2. Select [**Advanced**](https://manage.auth0.com/dashboard/#/tenant/advanced) in the top right corner. +3. Scroll down to the **Settings** section and enable the **Resource Parameter Compatibility Profile** toggle. \ No newline at end of file From 29a9ba6fa6d3364c7345846820170ce96115ae5e Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Tue, 28 Apr 2026 19:29:32 -0700 Subject: [PATCH 5/7] Final copyedits --- .../call-your-apis-on-users-behalf.mdx | 63 +++++++++++-------- .../exchange-access-token-js.mdx | 12 ++-- .../exchange-access-token-python.mdx | 6 +- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx index 86d137f42d..cbb8a48325 100644 --- a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx +++ b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx @@ -25,13 +25,9 @@ import RunMcpServerPython from "/snippets/mcp/get-started/call-your-apis/run-mcp import ExchangeAccessTokenJs from "/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx"; import ExchangeAccessTokenPython from "/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx"; - -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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. - - -Auth0 implements the On-Behalf-Of (OBO) token exchange ([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html)) to enable MCP servers to preserve user identity and permissions when calling downstream APIs. In this 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 upstream API on the user's behalf. +Auth0 implements the On-Behalf-Of (OBO) token exchange ([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html)) 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. +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. -By the end of this quickstart, you should have an MCP server that can: + +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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. + + +By the end of this quickstart, you will have: -* Exchange an Auth0 access token for a new access token with a different audience using the OBO token exchange. -* Verify the access token using the `@auth0/auth0-api-js` library (which uses `jose` under the hood) or the `auth0-api-python` library for Python against your tenant JWKS, enforcing issuer, audience, and RS256. +* An MCP server that calls your protected APIs on behalf of authenticated users using OBO token exchange +* Token verification configured to enforce security policies (issuer, audience, algorithm) +* A working example demonstrating the end-to-end OBO token exchange flow from client to MCP server to downstream API @@ -63,12 +64,12 @@ By the end of this quickstart, you should have an MCP server that can: ## Set up the Auth0 applications and APIs -To call downstream APIs, the MCP server uses the OBO token exchange to obtain an access token with the audience set to the API rather than the MCP server itself. The MCP server acts in a dual role: +The MCP server's dual role requires two configurations in Auth0: -1. To the MCP client (e.g., an IDE, an AI assistant), the MCP server acts as a resource server (an API). -2. To the underlying API (e.g., your own API), the MCP server acts as a client. +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 -As a result, you need to set up the MCP server twice on your Auth0 tenant, both as an API and as a client. +Let's set up both configurations. ### Create an API to represent your MCP server @@ -78,9 +79,12 @@ As a result, you need to set up the MCP server twice on your Auth0 tenant, both ### Create an application for your MCP server -To enable your MCP server to act as a client to obtain an Auth0 access token with the OBO token exchange, you need to create an application linked to your MCP server. The application needs to have the same identifier as your MCP server and an `app_type` set to `resource_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 code sample creates an application linked to your MCP server with the OBO token exchange grant enabled: +The following command creates an application linked to your MCP server with OBO token exchange enabled: ```shell wrap lines auth0 api post clients --data '{ @@ -94,20 +98,21 @@ auth0 api post clients --data '{ }' | jq -c '{client_id, client_secret}' > auth0-app-details.json ``` -If successful, Auth0 returns a JSON object with the `client_id` and `client_secret` of the newly-created MCP server application. These credentials will be used to configure the MCP server environment. +Save the `client_id` and `client_secret` from the output; you'll need them to configure your MCP server environment. -### Create a first-party API to call from the MCP server +### Create a downstream API -Create a protected first-party API that your MCP server tools can call on an authenticated user's behalf. +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 code sample configures a protected first-party API with the following characteristics: -- Enables [skipping user consent](https://auth0.com/docs/get-started/applications/third-party-applications/user-consent-and-third-party-applications#skip-consent-for-first-party-applications) -- Creates [client grants](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) for client and user-delegated access, enabling the MCP server application to call the API on both its own and a user's behalf +The following command creates an API with the following configurations: +- Client grants: Gives the MCP server [user-delegated and client access](https://auth0.com/docs/get-started/applications/application-access-to-apis-client-grants) the API +- Scope enforcement: Requires specific permissions (`read:private`) +- First-party status: Skips user consent for your own applications ```shell auth0 api post resource-servers --data '{ "identifier": "http://localhost:8787/", - "name": "Protected First Party API", + "name": "Protected Downstream API", "signing_alg": "RS256", "enforce_policies": true, "scopes": [ @@ -125,7 +130,7 @@ auth0 api post resource-servers --data '{ }' ``` -Save the `Audience` from the command output; you'll need it in a later step. +Save the API identifier (audience) from the output; you'll configure it in your environment variables as `API_AUTH0_AUDIENCE`. ## Sample app @@ -155,7 +160,11 @@ Save the `Audience` from the command output; you'll need it in a later step. - The sample app uses the OBO token exchange with a `greet` tool that calls your protected API on the authenticated user's behalf. + 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 @@ -199,7 +208,11 @@ Save the `Audience` from the command output; you'll need it in a later step. - The sample app uses the OBO token exchange with a `greet` tool that calls your protected API on the authenticated user's behalf. + 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 diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx index 0f0ae05ff4..b55962bc77 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-js.mdx @@ -1,8 +1,12 @@ 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. -### The Orchestrator: `bearerForUpstream()` +### Token exchange implementation -The process begins with the `bearerForUpstream()` function. It serves as a safe wrapper around our exchange logic by taking in the initial access token, managing the exchange process, and handling any potential errors: +The MCP server exchanges tokens in two steps: + +#### 1. Wrapper function: `bearerForUpstream()` + +This function safely handles the token exchange process and error handling: ```javascript wrap lines highlight={5} async function bearerForUpstream(accessToken: string) { @@ -23,9 +27,9 @@ async function bearerForUpstream(accessToken: string) { `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. -### The core logic: `exchangeTokenOnBehalfOf()` +#### 2. Exchange function: `exchangeTokenOnBehalfOf()` -`exchangeTokenOnBehalfOf()`, located in `src/auth0.ts`, contains the actual token exchange logic. It uses the `ApiClient` from the `@auth0/auth0-api-js` SDK to simplify the interaction with Auth0's `/oauth/token` endpoint. +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: diff --git a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx index e146fe0dad..4775c38ce7 100644 --- a/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx +++ b/auth4genai/snippets/mcp/get-started/call-your-apis/exchange-access-token-python.mdx @@ -1,7 +1,7 @@ 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. ### How tools use token exchange -Here's how the `greet` tool performs token exchange and calls the upstream API: +Here's how the `greet` tool performs the token exchange and calls the upstream API: ```python wrap lines highlight={10,11,12,13,14,17} @mcp.tool(name="greet") @@ -44,9 +44,9 @@ async def exchange_token_on_behalf_of(api_client, access_token: str) -> dict: return {"token": result["access_token"], "scopes": result.get("scope", "")} ``` -`exchange_token_on_behalf_of()` uses the `get_token_on_behalf_of()` method of `ApiClient` from the `auth0-api-python` SDK. It takes in the `access_token`, `audience`, and optional `scope` parameters, and upon a successful exchange, `returns the new access token and its associated scopes. +`exchange_token_on_behalf_of()` uses the `get_token_on_behalf_of()` method of `ApiClient` from the `auth0-api-python` SDK. It takes in the `access_token`, `audience`, and optional `scope` parameters, and upon a successful exchange, returns the new access token and its associated scopes. -### Client Configuration +### Client configuration The `ApiClient` is initialized in the `Auth0Middleware` (located in `src/auth0/middleware.py`) with the credentials of the MCP server application: From aaed9572c23285c405c3a470b8e3492414e26408 Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Wed, 29 Apr 2026 15:49:04 -0700 Subject: [PATCH 6/7] Copyedits --- .../mcp/get-started/call-your-apis-on-users-behalf.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx index cbb8a48325..844d9cbca8 100644 --- a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx +++ b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx @@ -43,13 +43,13 @@ To call your APIs on a user’s behalf, the MCP server must exchange the Auth0 a -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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. +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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. By the end of this quickstart, you will have: -* An MCP server that calls your protected APIs on behalf of authenticated users using OBO token exchange -* Token verification configured to enforce security policies (issuer, audience, algorithm) +* 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 From 91b2e5121da0068a570ef7b607d5bd642311450d Mon Sep 17 00:00:00 2001 From: Lucy Zhou Date: Thu, 30 Apr 2026 08:26:08 -0700 Subject: [PATCH 7/7] Updated Callout --- auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx index 844d9cbca8..d14ed6943d 100644 --- a/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx +++ b/auth4genai/mcp/get-started/call-your-apis-on-users-behalf.mdx @@ -42,9 +42,9 @@ To call your APIs on a user’s behalf, the MCP server must exchange the Auth0 a /> - + 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](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy/rate-limit-configurations/tier-100-rps-private-cloud), 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. - + By the end of this quickstart, you will have: