# Delegate Access With Token Exchange

Configure FusionAuth so that one service can exchange a User's access token for a token that calls another service on that User's behalf.

> For the index of this section of the site, see [llms.txt](https://fusionauth.io/docs/llms.txt)

> **PLAN:** This feature requires [an Enterprise plan](https://fusionauth.io/docs/get-started/core-concepts/plans-features.md#enterprise-features).

Available since `1.70.0`

This guide configures token exchange with the Entity policy engine. When you finish, a to-do service can exchange a User's access token for a token that targets an email service. The exchanged token identifies the User as the subject and the to-do service as the actor.

For background on how token exchange works, see [OAuth Token Exchange](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/token-exchange.md).

## Prerequisites

To follow this guide, you need:

*   FusionAuth `1.70.0` or later with an Enterprise license.
*   An Application that Users log in to with the Authorization Code grant, in the Tenant you plan to configure.
*   An [API key](https://fusionauth.io/docs/apis/authentication.md#managing-api-keys) with permission to call the Entity APIs. The examples store it in the `API_KEY` shell variable.

The examples use `http://localhost:9011` as the FusionAuth URL. If your instance has more than one Tenant, add an `X-FusionAuth-TenantId` header to each API request, or use an API key scoped to the Tenant.

## Enable Token Exchange on the Tenant

1.  Navigate to Tenants and click edit on your Tenant.
2.  Select the OAuth tab.
3.  In the Token exchange settings section, enable **Enable token exchange grant**.
4.  Set **Token exchange policy engine** to `Entity`.
5.  Leave **Max act claim depth** at `2`.
6.  Click save.

![The token exchange settings with the token exchange grant enabled.](https://fusionauth.io/img/docs/screenshots/tenant-token-exchange-enable.png)

To configure the Tenant with the API instead, set **`tenant.tokenExchangeConfiguration.enabled`** to `true` and **`tenant.tokenExchangeConfiguration.policyEngine`** to `ENTITY` with the [Tenant API](https://fusionauth.io/docs/apis/tenants.md).

## Create an Entity Type

Entities need an Entity Type. Create one for the services in this guide:

Create an Entity Type

```shell-session
$ curl -X POST http://localhost:9011/api/entity/type \
  -H "Authorization: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "entityType": { "name": "API Service" } }'
```

Save the **`entityType.id`** value from the response in the `ENTITY_TYPE_ID` shell variable.

The Entity Type name appears in exchanged tokens as the `sub_profile` member of the `act` claim, for example `fusionauth:API Service`.

## Create the Service Entities

Create an Entity for the email service. The email service is the audience of the exchanged token, so it doesn't need any token exchange settings:

Create the email service Entity

```shell-session
$ curl -X POST http://localhost:9011/api/entity \
  -H "Authorization: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity": {
      "name": "Email Service",
      "clientId": "email-service",
      "type": { "id": "'"$ENTITY_TYPE_ID"'" }
    }
  }'
```

Save the **`entity.id`** value from the response in the `EMAIL_ENTITY_ID` shell variable.

Create an Entity for the to-do service. The to-do service performs the exchange, so set **`entity.actAsTokenExchangeClient`** to `true`:

Create the to-do service Entity

```shell-session
$ curl -X POST http://localhost:9011/api/entity \
  -H "Authorization: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "entity": {
      "name": "To-do Service",
      "clientId": "todo-service",
      "clientSecret": "change-this-secret",
      "actAsTokenExchangeClient": true,
      "type": { "id": "'"$ENTITY_TYPE_ID"'" }
    }
  }'
```

Save the **`entity.id`** value from the response in the `TODO_ENTITY_ID` shell variable.

## Grant the To-do Service Access to the Email Service

The Entity policy engine allows an exchange when the exchanger has a Grant on the audience Entity. Create a Grant on the email service for the to-do service:

Grant the to-do service access to the email service

```shell-session
$ curl -X POST http://localhost:9011/api/entity/$EMAIL_ENTITY_ID/grant \
  -H "Authorization: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "grant": { "recipientEntityId": "'"$TODO_ENTITY_ID"'" } }'
```

The Entity policy engine only checks that the Grant exists, so this Grant doesn't need any permissions.

## Get a User Access Token

Log in as a User through your Application with the [Authorization Code grant](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth.md#example-authorization-code-grant). Save the access token from the Token endpoint response in the `USER_ACCESS_TOKEN` shell variable.

In a real deployment, the to-do service receives this token in the `Authorization` header of a request from the User's client.

## Exchange the Token

Call the Token endpoint as the to-do service. Pass the User's access token as the subject token and the email service's client Id as the audience:

Exchange the User's access token

```shell-session
$ curl -X POST http://localhost:9011/oauth2/token \
  -u "todo-service:change-this-secret" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=$USER_ACCESS_TOKEN" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "audience=email-service"
```

FusionAuth returns a new access token:

Example token exchange response

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjFlNTZhOWRlMyJ9...",
  "expires_in": 3599,
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "scope": "openid offline_access",
  "token_type": "Bearer"
}
```

The Entity policy engine copies the scopes from the subject token, so the `scope` value matches the User's original access token.

If the request fails, FusionAuth returns an [OAuth error](https://fusionauth.io/docs/apis/oauth/oauth-error.md). The **`error_description`** value explains the cause. For example, if you omit the `audience` parameter, FusionAuth returns:

Example error response

```json
{
  "error": "invalid_request",
  "error_description": "The Entity Policy Engine requires an audience parameter.",
  "error_reason": "audience_required"
}
```

## Check the Issued Token

Decode the issued access token. Its claims look like the following:

Example claims in the exchanged token

```json
{
  "act": {
    "sub": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
    "sub_profile": "fusionauth:API Service"
  },
  "amr": ["pwd"],
  "applicationId": "85a03867-dccf-4882-adde-1a79aeec50df",
  "aud": ["f81d4fae-6dec-11d0-a765-00a0c91e6bf7"],
  "auth_time": 1792680000,
  "authenticationType": "PASSWORD",
  "client_id": "todo-service",
  "exp": 1792683600,
  "gty": ["authorization_code", "urn:ietf:params:oauth:grant-type:token-exchange"],
  "iat": 1792680000,
  "iss": "http://localhost:9011",
  "jti": "6b3a7d1e-4c8f-4b2a-9e55-0f1c2d3e4a5b",
  "scope": "openid offline_access",
  "sid": "3f6c1b2a-8d4e-4f7a-9b0c-1d2e3f4a5b6c",
  "sub": "c0a6ae6c-6d38-4bb5-a7c2-4d2b4e3a1f2d",
  "tid": "d7d09513-a3f5-401c-9685-34ab6c552453",
  "tty": "at"
}
```

The `sub` claim still identifies the User, and `aud` now names the email service. The `act` claim identifies the to-do service as the actor. The `amr`, `applicationId`, `auth_time`, `authenticationType`, and `sid` claims come from the User's original token.

When the email service receives this token, it should:

1.  Verify the signature with the keys from FusionAuth's [JSON Web Key Set](https://fusionauth.io/docs/apis/oauth/jwks.md).
2.  Check that `iss` matches the FusionAuth issuer, `aud` contains `email-service`, and `exp` is in the future.
3.  Authorize the request based on the User in `sub` and the actor in `act.sub`.

## View the Audit Log Entry

FusionAuth records each exchange attempt. Navigate to System -> Token Exchange Log to view the entry for the exchange you just made, including the policy decision and the issued token's actor chain.

![The Token Exchange Log with an entry for an exchange.](https://fusionauth.io/img/docs/screenshots/token-exchange-log.png)

## Next Steps

*   To narrow scopes, apply time-based rules, or call an external authorization service, switch to the Lambda policy engine and write a [Token Exchange Policy lambda](https://fusionauth.io/docs/extend/code/lambdas/token-exchange-policy.md).
*   To add claims to exchanged tokens, write a [Token Exchange Populate lambda](https://fusionauth.io/docs/extend/code/lambdas/token-exchange-populate.md).
*   To accept tokens from another Identity Provider, add its issuer to **Allowed issuers** and send its tokens with the `urn:ietf:params:oauth:token-type:jwt` subject token type.

Related

[Lifecycle OAuth Token Exchange Learn how FusionAuth implements OAuth 2.0 Token Exchange (RFC 8693) so that a service can call another service on behalf of a user. token exchangerfc 8693delegation](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/token-exchange.md)

[APIs Retrieve a Token Exchange Chain API API documentation for the FusionAuth Retrieve a Token Exchange Chain API. token exchangerfc 8693](https://fusionauth.io/docs/apis/system/retrieve-a-token-exchange-chain.md)

[APIs Search the Token Exchange Audit Log API API documentation for the FusionAuth Search the Token Exchange Audit Log API. token exchangerfc 8693](https://fusionauth.io/docs/apis/system/search-the-token-exchange-audit-log.md)

[APIs Issue a JWT API API documentation for the FusionAuth Issue a JWT API. token exchange](https://fusionauth.io/docs/apis/jwt/issue-a-jwt.md)
---

## Other pages in OAuth

> For the full index of this section, see [Lifecycle](https://fusionauth.io/docs/llms-lifecycle.txt).

- [CIMD Registration Caching And Lifecycle](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/cimd-registration-lifecycle.md): Learn how long FusionAuth caches a Client ID Metadata Document, what happens when it expires, and how a CIMD client registration is refreshed or removed.
- [OAuth Client Registration | Static and CIMD](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/client-registration.md): Learn how FusionAuth registers OAuth clients, either ahead of time as Applications or just-in-time from a Client ID Metadata Document.
- [OAuth DPoP](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/dpop.md): Learn how to enable sender-constrained OAuth tokens DPoP (Demonstration of Proof-of-Possession) with FusionAuth and validate DPoP proofs in your APIs.
- [Host A Client ID Metadata Document (CIMD)](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/host-cimd-document.md): Publish a Client ID Metadata Document so your OAuth client can log in to FusionAuth without being registered ahead of time.
- [OAuth](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth.md): An overview of how FusionAuth provides an OAuth 2.0 and OpenID Connect SSO login system.
- [OAuth Issuer Validation](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/issuer-validation.md): Learn how to validate the authorization response issuer parameter (RFC 9207) to prevent mix-up attacks.
- [Modes](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/modes.md): An overview of OAuth modes and how OAuth is commonly used.
- [OIDC Prompt](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/prompt.md): Learn about OpenID Connect prompt and example use cases.
- [OAuth Response Modes](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/response-modes.md): Learn about OAuth 2.0 response modes (query, fragment, form_post) and when to use each one.
- [Access Control with OAuth Scopes](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/scopes.md): Learn about OAuth scope policy configuration, managing custom scopes, and using scopes in an OAuth2 workflow.
- [OAuth Token Exchange](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/token-exchange.md): Learn how FusionAuth implements OAuth 2.0 Token Exchange (RFC 8693) so that a service can call another service on behalf of a user.
- [Manage Software Tokens](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/tokens.md): Learn about OAuth2 and OpenID Connect Tokens and how they are used.
- [URL Validation](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth/url-validation.md): Learn about OAuth URL validation policies in FusionAuth.
