Access Token Exchange with Token Vault

Token Vault supports the access token exchange, which implements the OAuth 2.0 Token Exchange Protocol. The access token exchange enables a client to exchange an Auth0 access token (subject token) for an external provider’s access token (requested token). 

When a Single-Page Application (SPA) calls a backend API, it only passes an Auth0 access token in the Authorization header. Because the backend API does not receive any refresh tokens issued to the SPA, it cannot use the refresh token exchange to access the Token Vault to call external APIs. 

Instead, the backend API can exchange the Auth0 access token received from the SPA (subject token) for an external provider’s access token (requested token), otherwise known as the access token exchange. This process keeps the sensitive external credentials secure on the backend.

In Auth0’s access token exchange, the backend API acts as both a client and a resource server

  • Client: Uses its own credentials to securely perform the access token exchange with the Auth0 Authorization Server. In Auth0, you create a Custom API Client with the same identifier as the backend API. The backend API passes the Custom API Client’s credentials to securely perform the access token exchange with the Auth0 Authorization Server.

  • Resource server: Serves the backend API to the SPA and validates the Auth0 access token.

By acting as the intermediary between the SPA and Auth0 Authorization Server, the backend API prevents unauthorized clients from stealing the Auth0 token and using it to access the external provider on the user's behalf.

Use cases

Common use cases for the access token exchange include:

  • A backend API: A user interacts with an SPA, which then calls a backend API to exchange an Auth0 access token for an external provider’s access token with the Auth0 Authorization Server. 

  • Microservice architecture: Backend services, such as MCP servers or other OAuth 2.0 resource servers, that need to exchange access tokens to access external APIs. 

How it works

The following sequence diagram describes end-to-end how to call external APIs using the access token exchange in Auth0:

Let’s walk through a real-world example: A user wants to schedule a meeting in their Google Calendar using an SPA.

Prerequisites

Before getting started, you must configure the access token exchange with Token Vault.

Step 1: Authenticate user and authorize access

To schedule the meeting, the SPA needs to authenticate the user with Google via Auth0 and then receive the user’s permission to access the Google Calendar API.

1. When the user logs into the SPA via Google using the social login flow, the Auth0 SDK makes a GET request to the /authorize endpoint with the following additional parameters:

Parameter Description
connection The name of the external provider. In this case, google-oauth2.
connection_scope Requests additional scopes to be authorized for the connection. In this case, it includes the Google Calendar API scopes.

Note: At runtime, the list of connection scopes is merged with the scopes you statically configured for the connection. Whenever the user is redirected to authorize this connection, Auth0 will always request the scopes you selected. To learn more, read Configure Token Vault.

scope Requests Auth0 scopes to be authorized for the application. Include openid and profile.
audience Set to the audience of the backend API that your SPA will request access to i.e. https://my-api.example.com.

2. The Auth0 Authorization Server redirects the user to the Google consent prompt. The user authenticates and authorizes the Google connection, giving the SPA permission to access the Google Calendar API. 

3. The Auth0 Authorization Server redirects the user back to the SPA with the single-use authorization code.

4. The SPA calls the Auth0 SDK to make a POST request to the /oauth/token endpoint with the authorization code, the application's client ID, and the application's credentials. 

5. The Auth0 Authorization Server verifies the request and responds with an Auth0 access token and ID token. The Auth0 access token is scoped for the backend API with the same audience claim as the backend API’s unique identifier. The SPA stores the Auth0 access token and includes it in the Authorization header of all subsequent requests it makes to the backend API.

6. The Auth0 Authorization Server stores the Google access token in a secure tokenset within the user's Token Vault.

Step 2: SPA calls backend API with Auth0 access token

When the SPA calls the backend API, it passes the Auth0 access token in the Authorization header to the backend API. The backend API validates the Auth0 access token by checking the following: 

  • Signature: Verify the token's signature using Auth0's public key. This confirms that Auth0 issued the access token. 

  • Issuer: Checks the iss claim in the token's payload to confirm that the token was issued by your Auth0 tenant.

  • Audience: Checks the aud claim to ensure it matches the unique identifier of the backend API itself. This confirms that the token was specifically issued for this resource server.

  • Expiration: Validates the exp claim to ensure the token is still valid and has not expired.

  • Scopes: Checks the scope claim to determine what permissions the user has been granted.

After successfully completing these checks, the backend API can trust the Auth0 access token and proceed with the token exchange.

Step 3: Backend API performs access token exchange

For the access token exchange, you need to create a Custom API Client linked to the backend API. The Custom API Client has the same identifier as your backend API and has the Token Vault grant type enabled. 

When the backend API performs the access token exchange, it authenticates itself by passing the Custom API Client’s credentials to the Auth0 Authorization Server, proving that it is the same entity that was registered in the Auth0 Dashboard.

To perform the access token exchange, the backend API calls Auth0 SDKs to make a POST request to the /oauth/token endpoint. 

In the token request, the backend API:

  1. Passes the backend API’s (the Custom API Client’s) client credentials to the Auth0 Authorization Server to authenticate itself. 

  2. Exchange an Auth0 access token for a Google access token.

curl --request POST 'https://{yourDomain}/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
	"client_id": "<YOUR_CUSTOM_API_CLIENT_ID>",
	"client_secret": "<YOUR_CUSTOM_API_CLIENT_SECRET>",
	"subject_token": "<YOUR_AUTH0_ACCESS_TOKEN>",
	"grant_type": "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token",
	"subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
	"requested_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token",
	"connection": "google-oauth2"
}'

Was this helpful?

/

Parameter Description
grant_type The grant type. Set to the Token Vault grant type: urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token.
client_id The Custom API client ID.
client_secret The Custom API client secret.
subject_token_type The type of subject token. For the access token exchange, set to the access token: urn:ietf:params:oauth:token-type:access_token.
subject_token The Auth0 access token that the Auth0 Authorization Server validates to identify the user.
requested_token_type The requested token type. For Token Vault, set to the external provider’s access token: http://auth0.com/oauth/token-type/federated-connection-access-token.
connection The connection name, in this case, google-oauth2.
login_hint (Optional) Only use login_hint if the user has several accounts from the same connection, such as a work Google account and a personal Google account. If you pass in a value for login_hint during the token exchange, you explicitly identify which of the user's multiple linked accounts the token request is for.

Step 4: Auth0 Authorization Server validates access token

The Auth0 Authorization Server validates and loads the user profile associated with the Auth0 access token:

  • Auth0 verifies that the client making the access token exchange request is linked to the backend API identified by the audience of the access token.

  • Auth0 checks if the user profile’s identities array contains a user account with the connection name passed in the authorization request. 

  • If the authorization request contains login_hint, Auth0 looks for an identity matching both the connection name and the login_hint

  • If Auth0 can’t find the user, it returns a 401 status code with an error message. 

Once the Auth0 Authorization Server validates the user, it locates the Google access token within the Token Vault. If it is still valid, Auth0 returns the Google access token with its scopes and expiry time:

{
   "access_token": "<YOUR_GOOGLE_ACCESS_TOKEN>",
   "scope": "https://www.googleapis.com/auth/calendar         https://www.googleapis.com/auth/calendar.addons.execute https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.events.readonly https://www.googleapis.com/auth/calendar.settings.readonly https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
   "expires_in": 1377,
   "issued_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token",
   "token_type": "Bearer"
}

Was this helpful?

/

Using the Google access token, the backend API calls the Google Calendar API on the user’s behalf to schedule the meeting.