Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions main/docs/authenticate/custom-token-exchange.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ReleaseStageNotice } from "/snippets/ReleaseStageNotice.jsx"
<ReleaseStageNotice
feature="Custom Token Exchange (CTE)"
stage="ea"
plans="B2B Professional and Enterprise"
plans="B2C Professional, B2B Professional, and Enterprise"
terms="true"
/>

Expand All @@ -16,6 +16,7 @@ Custom Token Exchange enables applications to exchange their existing tokens for
* Getting Auth0 tokens for another <Tooltip tip="Audience: Unique identifier of the audience for an issued token. Named aud in a token, its value contains the ID of either an application (Client ID) for an ID Token or an API (API Identifier) for an Access Token." cta="View Glossary" href="/docs/glossary?term=audience">audience</Tooltip>
* Integrating with an external <Tooltip tip="Identity Provider (IdP): Service that stores and manages digital identities." cta="View Glossary" href="/docs/glossary?term=identity+provider">identity provider</Tooltip>
* Migrating to Auth0
* Delegated authorization: A principal (such as a service, AI agent, or support agent) acts on behalf of a user

To learn more, read [Example Use Cases and Code Samples](/docs/authenticate/custom-token-exchange/cte-example-use-cases).

Expand All @@ -29,7 +30,7 @@ You can [configure multiple Custom Token Exchange Profiles](/docs/authenticate/c
<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
Custom Token Exchange gives you the added flexibility to set the user for the transaction by taking on the additional responsibility of securely validating the corresponding subject token that identifies the user for the transaction.

Remember that subject tokens used with Custom Token Exchange can be any token format or type you require, as long as your Action code can interpret them. **You must implement strong validation of the tokens you receive and accept.** If you fail to do so, you open yourself up to different attack vectors, such as spoofing or replay attacks, resulting in bad actors being able to authenticate with someone else’s user ID.
Remember that subject and actor tokens used with Custom Token Exchange can be any token format or type you require, as long as your Action code can interpret them. **You must implement strong validation of the tokens you receive and accept.** If you fail to do so, you open yourself up to different attack vectors, such as spoofing or replay attacks, resulting in bad actors being able to authenticate with someone else’s user ID or acting on their behalf in an unauthorized manner.

To learn about different options for implementing secure validation of your subject tokens, read and apply the recommendations included in [Example Use Cases and Code Samples](/docs/authenticate/custom-token-exchange/cte-example-use-cases). Make sure you also take into consideration and apply [Attack Protection](/docs/authenticate/custom-token-exchange/cte-attack-protection) capabilities.
</Callout>
Expand All @@ -41,6 +42,8 @@ Each Custom Token Exchange transaction generates a tenant event log:
* Successful transactions: `secte` logs
* Failed transactions: `fecte` logs

When an actor is set via `setActor()`, the `actor` property (containing `sub` and any nested `actor`) is included in `secte` log entries for audit purposes.

Use the tenant logs to help you troubleshoot any issues you encounter with your token exchange.

## Limitations
Expand All @@ -49,6 +52,5 @@ Custom Token Exchange does not support the following:

* MFA API methods `api.authentication.challengeWith()` and `api.authentication.EnrollWith()`
* Custom DB Connections with import mode `ON` are not supported for `setUserByConnection()` operations
* Specific delegation support (e.g. `actor_token` and `actor` claim)
* Third-party and non-OIDC conformant clients
* The target API must have **Allow Skipping User Consent** enabled since consent cannot be collected in a non-interactive flow.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ReleaseStageNotice } from "/snippets/ReleaseStageNotice.jsx"
<ReleaseStageNotice
feature="Custom Token Exchange (CTE)"
stage="ea"
plans="B2B Professional and Enterprise"
plans="B2C Professional, B2B Professional, and Enterprise"
terms="true"
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ReleaseStageNotice } from "/snippets/ReleaseStageNotice.jsx"
<ReleaseStageNotice
feature="Custom Token Exchange (CTE)"
stage="ea"
plans="B2B Professional and Enterprise"
plans="B2C Professional, B2B Professional, and Enterprise"
terms="true"
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ReleaseStageNotice } from "/snippets/ReleaseStageNotice.jsx"
<ReleaseStageNotice
feature="Custom Token Exchange (CTE)"
stage="ea"
plans="B2B Professional and Enterprise"
plans="B2C Professional, B2B Professional, and Enterprise"
terms="true"
/>

Expand Down Expand Up @@ -338,6 +338,110 @@ curl --location 'https://{yourDomain}.auth0.com/oauth/token' \

Read [code samples](#code-samples) for a more detailed example on how to validate opaque refresh token with the legacy IdP.

### Use Case: Support agent acting on behalf of an end user

GearUp's support agents need to access end-user data and perform actions via GearUp's backend APIs on behalf of the end-user. The support tool authenticates the agent, then uses the Custom Token Exchange to obtain an access token representing the end-user with the agent tracked as the actor.

<Frame>![](/docs/images/custom-token-exchange/Support-agent-acting-on-behalf-of-an-end-user.png)</Frame>

In this case, the agent's Auth0 ID token is sent as the `actor_token` in the request, and a signed JWT identifying the end user is sent as the `subject_token`. When `actor_token_type` is set to `urn:ietf:params:oauth:token-type:id_token`, Auth0 automatically validates the token (signature, expiry, issuer) and populates `event.transaction.actor_token_user` with the agent's profile. This eliminates the need for custom validation code for the actor token.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
Using an Auth0 ID token as the `actor_token` is not mandatory. When `actor_token_type` is a custom value, the Action must validate the actor token via custom code, similar to how subject tokens are validated. The automatic population of `event.transaction.actor_token_user` only applies to Auth0 ID tokens.
</Callout>

1. The support tool authenticates the agent with Auth0 and obtains the agent's ID token.
2. The support tool calls Auth0's `/oauth/token` using a [Custom Token Exchange request](/docs/get-started/authentication-and-authorization-flow/token-exchange-flow/call-your-api-using-the-custom-token-exchange-flow), including a signed JWT with the end-user's identifier as `subject_token` and the agent's ID token as `actor_token`.
3. The [Custom Token Exchange Action](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger) validates the subject token, verifies the actor has the right to act on behalf of the end user, and calls `api.authentication.setActor()`.
4. Auth0 issues tokens with the `act` claim identifying the support agent.
5. The support agent consumes the API on behalf of the end user. APIs can inspect the `act` claim to apply authorization policies specific to delegated access, such as restricting write operations or logging activity for audit purposes.

The Custom Token Exchange Action decides what to include in the actor object, including any custom properties and nesting levels. See the [Custom Token Exchange API Object documentation](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger/custom-token-exchange-api-object) for constraints.

```javascript lines expandable
const jwksUri = "https://gearup.com/.well-known/jwks.json";

/**
* Handler to be executed while executing a custom token exchange request
* @param {Event} event - Details about the incoming token exchange request.
* @param {CustomTokenExchangeAPI} api - Methods and utilities to define token exchange process.
*/
exports.onExecuteCustomTokenExchange = async (event, api) => {

// 1. VALIDATE the end-user token received in the subject_token
const { isValid, payload } = await validateToken(event.transaction.subject_token);

if (!isValid) {
api.access.rejectInvalidSubjectToken("Invalid subject_token");
return;
}

// 2. AUTHORIZE the actor — verify the agent has the right to act on behalf of this end-user
const actorUser = event.transaction.actor_token_user;
if (!actorUser) {
api.access.deny("invalid_request", "Actor token is required for this profile");
return;
}

const isAuthorized = await checkDelegationPolicy(actorUser.user_id, payload.sub);
if (!isAuthorized) {
api.access.deny("unauthorized_actor", "Agent is not authorized to act on behalf of this user");
return;
}

// 3. SET THE ACTOR to include the act claim in issued tokens
api.authentication.setActor({
sub: actorUser.user_id,
sub_profile: "human",
role: "support"
});

// 4. SET THE USER for the transaction (the end-user being acted upon)
api.authentication.setUserById(payload.sub);

async function validateToken(subjectToken) {
// Add your code here. REFER TO CODE SAMPLES FOR DETAILED EXAMPLES
}

async function checkDelegationPolicy(agentId, userId) {
// Implement your delegation policy check here.
// For example, verify the agent belongs to the support team
// and is assigned to this user's region.
return true;
}
};
```

The issued access token will include the `act` claim:

```json lines
{
"sub": "auth0|end_user_id",
"aud": "https://api.gearup.com",
"act": {
"sub": "auth0|support_agent_id",
"sub_profile": "human",
"role": "support"
}
}
```

### Important considerations for delegated authorization

When implementing delegated authorization with Custom Token Exchange, follow these guidelines:

* **Implement authorization logic within your Custom Token Exchange Action** to verify that the actor is authorized to access the specific user account. For example, you may wish to implement authorization decisions that only specific actors are allowed to perform delegated access, or you could check that the target user has an active support ticket open to defend against arbitrary user access.

* **Validate the requested scopes** to ensure that only the minimal set of required scopes for delegated authorization will be issued. You may wish to ensure that some sensitive operations can never be performed in the context of delegated authorization.

* **Ensure your APIs make use of the delegation context** in the `act` claim of the access token. You should keep audit logs in your APIs of actions performed by a delegated actor, and ensure you can clearly audit which actor performed operations on behalf of the user.

* **For auditing purposes**, you can make use of the actor details in Auth0 tenant logs. Successful Custom Token Exchange transactions (`secte` log events) include the `actor` property with the `sub` and any nested `actor` information.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
Auth0 does not notify the end-user when a delegated authorization token is issued on their behalf. If your use case requires user notification or explicit consent before delegated access takes place, consider using [Client Initiated Backchannel Authentication (CIBA)](/docs/get-started/authentication-and-authorization-flow/client-initiated-backchannel-authentication-ciba-flow) to push a consent request to the end-user's device prior to performing the token exchange. For simpler notification needs, you can implement notification logic within your Custom Token Exchange Action, a Post-Login Action, or in your downstream services.
</Callout>

## Code samples

The following code samples show best practices for common scenarios for validating incoming subject tokens in a secure and performant way.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import { ReleaseStageNotice } from "/snippets/ReleaseStageNotice.jsx"
<ReleaseStageNotice
feature="Custom Token Exchange (CTE)"
stage="ea"
plans="B2B Professional and Enterprise"
plans="B2C Professional, B2B Professional, and Enterprise"
terms="true"
/>

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
MFA is not compatible with Custom Token Exchange transactions where an actor is set via `api.authentication.setActor()`. If MFA is required (via policy or Post-Login Action) and the Custom Token Exchange Action sets an actor, the transaction fails with a `400` error: `MFA is not supported using actor_token with the requested token exchange profile.`
</Callout>

To add protection against token theft or other security risks, you can add Multi-factor Authentication to a Custom Token Exchange request using one of the following methods:

* [Enable and configure your MFA policy ](/docs/secure/multi-factor-authentication/enable-mfa) for your tenant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: Custom Token Exchange Trigger

<Warning>

Custom Token Exchange (CTE) is currently available in Early Access for all Auth0 Enterprise and B2B Pro customers. By using this feature, you agree to the applicable Free Trial terms in [Okta’s Master Subscription Agreement](https://www.okta.com/legal/). To learn more about Auth0’s product release cycle, read [Product Release Stages](/docs/troubleshoot/product-lifecycle/product-release-stages). To learn more about subscription types, review the Auth0 [pricing](https://auth0.com/pricing) page.
Custom Token Exchange (CTE) is currently available in Early Access for all Auth0 B2C Professional, B2B Professional, and Enterprise customers. By using this feature, you agree to the applicable Free Trial terms in [Okta’s Master Subscription Agreement](https://www.okta.com/legal/). To learn more about Auth0’s product release cycle, read [Product Release Stages](/docs/troubleshoot/product-lifecycle/product-release-stages). To learn more about subscription types, review the Auth0 [pricing](https://auth0.com/pricing) page.

</Warning>

Expand All @@ -27,6 +27,16 @@ Custom Token Exchange Early Access doesn’t support `api.authentication.challe

</Callout>

## Actor support for delegation

The `custom-token-exchange` trigger supports delegated authorization scenarios. The Action can call [`api.authentication.setActor()`](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger/custom-token-exchange-api-object#api-authentication-setactor-actor) to include an `act` claim in issued tokens, identifying the principal acting on behalf of the subject user.

`setActor()` can be called regardless of whether the request includes an `actor_token`. The `actor_token` parameter is one way for the calling application to pass actor information to the Action, but the actor identity can also be embedded in the `subject_token` itself or obtained through other custom means within the Action code.

When the request does include `actor_token` and `actor_token_type`, these values are available in [`event.transaction`](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger/custom-token-exchange-event-object#event-transaction). If `actor_token_type` is `urn:ietf:params:oauth:token-type:id_token` and the token is a valid Auth0 ID token from the same tenant, the server automatically validates the token and populates `event.transaction.actor_token_user` with the actor’s user profile.
Comment thread
lrzhou25 marked this conversation as resolved.

The `act` claim set via `setActor()` is also available in Post-Login Actions via [`event.transaction.actor`](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/post-login-event-object#event-transaction).

## Triggers

### Custom Token Exchange
Expand All @@ -36,7 +46,7 @@ The `custom-token-exchange` trigger is the first step in the Auth0 pipeline. Thi
### References

* [Event object](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger/custom-token-exchange-event-object): Provides contextual information about the a Token Exchange request, including the subject_token, IP address, client, and more.
* [API object](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger/custom-token-exchange-api-object): Provides methods to use with your Token Exchange Action that allows you to set the user, reject the transaction, set user metadata and access the Actions cache, and more.
* [API object](/docs/customize/actions/explore-triggers/signup-and-login-triggers/custom-token-exchange-trigger/custom-token-exchange-api-object): Provides methods to use with your Token Exchange Action that allows you to set the user, set the actor for delegation, reject the transaction, set user metadata and access the Actions cache, and more.

## Common Use Cases

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ The Custom Token Exchange flow is governed by a single, dedicated [Custom Token
As a prerequisite for the flow, you must define a [Custom Token Exchange Profile](/docs/authenticate/custom-token-exchange/configure-custom-token-exchange#create-custom-token-exchange-profile) that maps a specific `subject_token_type` to the Action that will handle the requests.

When the Custom Token Exchange flow is triggered:
1. The application sends a `POST` request to the `/oauth/token` endpoint with the security token to be exchanged (the `subject_token`) and its corresponding `subject_token_type`.
1. The application sends a `POST` request to the `/oauth/token` endpoint with the security token to be exchanged (the `subject_token`) and its corresponding `subject_token_type`. The request can optionally include an `actor_token` and `actor_token_type` to identify a principal acting on behalf of the user.
2. Auth0 validates the client request and credentials.
3. The associated Custom Token Exchange Action is executed. Your custom code must decode and validate the `subject_token`, enforce any necessary authorization policies, and set the user for the transaction to approve the exchange.
3. The associated Custom Token Exchange Action is executed. Your custom code must decode and validate the `subject_token`, enforce any necessary authorization policies, and set the user for the transaction to approve the exchange. The Action can also set an actor for the transaction via `api.authentication.setActor()`, which results in an `act` claim in the issued tokens that identifies who is acting on behalf of the user.
4. The rest of the standard Auth0 pipeline executes to generate an Auth0 access token and optionally an ID token and refresh token for the set user.
5. The requesting application can then use these newly issued tokens to securely call APIs on behalf of the user that was set in the Action.

Expand Down
Loading
Loading