Skip to content
Open
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
9 changes: 9 additions & 0 deletions main/config/navigation/authenticate.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@
"docs/authenticate/single-sign-on/native-to-web/native-to-web-sso-best-practices"
]
},
{
"group": "Session Delegation",
"root": "docs/authenticate/single-sign-on/session-delegation",
"pages": [
"docs/authenticate/single-sign-on/session-delegation/configure-session-delegation",
"docs/authenticate/single-sign-on/session-delegation/implement-session-delegation",
"docs/authenticate/single-sign-on/session-delegation/session-delegation-behavior-and-monitoring"
]
},
"docs/authenticate/single-sign-on/api-endpoints-for-single-sign-on",
"docs/authenticate/single-sign-on/okta-access-gateway"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,18 @@ Custom Token Exchange is currently available in Early Access. By using this feat
</Note>

### Remarks
- The scopes issued to the application may differ from the scopes requested. In this case, a scope parameter will be included in the response JSON. Scopes are filtered out as explained in [API Access Policies for Applications](https://auth0.com/docs/ja-jp/get-started/apis/api-access-policies-for-applications)

- The `subject_token_type` must correspond to an existing Custom Token Exchange Profile and an associated [Action](https://auth0.com/docs/customize/actions).
Read [Custom Token Exchange](/docs/authenticate/custom-token-exchange) to understand Custom Token Exchange Profiles, how a profile maps to an Action, and how to enable this flow for your application.

- For non-confidential applications that cannot keep the Client Secret safe (for example, native apps), the endpoint supports passing no Client Secret but the application itself must have the property `tokenEndpointAuthMethod` set to `none`. You can do this either from the UI (**Dashboard > Applications > Application Settings**) or using the Management API.
- The scopes issued to the application may differ from the scopes requested. In this case, a scope parameter will be included in the response JSON. Scopes are filtered out as explained in [API Access Policies for Applications](/docs/get-started/apis/api-access-policies-for-applications).

- You have to [Enable Custom Token Exchange for your application](https://auth0.com//docs/authenticate/custom-token-exchange/configure-custom-token-exchange#enable-custom-token-exchange-for-your-application). To learn more, read the [Custom Token Exchange documentation](https://auth0.com/docs/authenticate/custom-token-exchange).
- MFA is not compatible with transactions where an actor is set via `setActor()`. In those cases, the request returns a `400` error: `MFA is not supported using actor_token with the requested token exchange profile.`

- `actor_token` and `actor_token_type` must both be present or both absent. Providing only one returns a `400` error with code `invalid_request`.
- When the Action calls `setActor()`, issued access tokens and ID tokens include an `act` claim representing the [delegation chain](/docs/secure/call-apis-on-users-behalf/on-behalf-of-token-exchange#the-act-claim). The `act` claim is also included in the `userinfo` response.

- When `actor_token` is present, refresh tokens are not issued. The `offline_access` scope is excluded from the response.
- To request a Session Transfer Token instead of an access token, set `audience` to `urn:YOUR_AUTH0_TENANT_DOMAIN:session_transfer`. Calling `setActor()` is required for this request. Read [Session Delegation](/docs/authenticate/single-sign-on/session-delegation) to learn how to redeem the resulting token to establish a session on behalf of the subject user.

- MFA is not compatible with transactions where an actor is set via `setActor()`. If MFA is required and the Custom Token Exchange Action sets an actor, the request returns a `400` error: `MFA is not supported using actor_token with the requested token exchange profile.`

- When the Action calls `setActor()`, issued access tokens and ID tokens include an `act` claim representing the [delegation chain](/docs/secure/call-apis-on-users-behalf/on-behalf-of-token-exchange#the-act-claim). The `act` claim is also included in the userinfo response.
- To include standard claims (such as `email`) in the issued token(s), the corresponding scope (`email`) must be included in the request, even if your Action sets the claim explicitly. To learn more, read [OpenID Connect Scopes: Standard claims](/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims).

## Headers
<ParamField header="DPoP" type="string">
Expand Down Expand Up @@ -69,7 +66,7 @@ Custom Token Exchange is currently available in Early Access. By using this feat
</ParamField>

<ParamField body="audience" type="string">
(Optional) The unique identifier of the target API you want to access. The default tenant audience will be used when not present, as configured in [Tenant Settings](https://auth0.com/docs/get-started/tenant-settings).
(Optional) The unique identifier of the target API you want to access. The default tenant audience will be used when not present, as configured in [Tenant Settings](https://auth0.com/docs/get-started/tenant-settings). To request a [Session Transfer Token](/docs/authenticate/single-sign-on/session-delegation) for delegated session establishment instead of an access token, use `urn:YOUR_AUTH0_TENANT_DOMAIN:session_transfer`.
</ParamField>

<ParamField body="resource" type="string">
Expand Down Expand Up @@ -112,7 +109,7 @@ Custom Token Exchange is currently available in Early Access. By using this feat
</ResponseField>

<ResponseField name="issued_token_type" type="string">
The issued token type.
The issued token type. For a standard exchange, this is `urn:ietf:params:oauth:token-type:access_token`. When requesting a Session Transfer Token, this is `urn:auth0:params:oauth:token-type:session_transfer_token` and the token itself is returned in `access_token`.
</ResponseField>

<ResponseField name="expires_in" type="integer">
Expand All @@ -124,3 +121,4 @@ Custom Token Exchange is currently available in Early Access. By using this feat
| Status | Description |
|--------|-------------|
| 200 | Successful response |
| 400 | Bad request. May occur if required parameters are missing or inconsistent (for example, only one of `actor_token`/`actor_token_type` is present), or if `setActor()` was not called when requesting a Session Transfer Token. |
4 changes: 4 additions & 0 deletions main/docs/authenticate/custom-token-exchange.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Custom Token Exchange enables applications to exchange their existing tokens for

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

<Tip>
If you don't have custom token validation requirements and only need to propagate delegation context between your own services, use [On-Behalf-Of Token Exchange](/docs/secure/call-apis-on-users-behalf/on-behalf-of-token-exchange) instead. It doesn't run a custom Action per request and supports significantly higher throughput. Use Custom Token Exchange when you need to validate subject or actor tokens in a custom format, integrate an external identity provider, or otherwise need full control over the exchange logic.
</Tip>

Each Custom Token Exchange request maps to a [Custom Token Exchange Profile](/docs/authenticate/custom-token-exchange/configure-custom-token-exchange#create-custom-token-exchange-profile) that is governed by an [Action](/docs/customize/actions), where you can:

* Write custom code to decode and validate `subject_tokens` passed to the `/oauth/token` endpoint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ 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
### Use Case: Support agent acting on behalf of an end user via API

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.

Expand Down Expand Up @@ -426,7 +426,7 @@ The issued access token will include the `act` claim:
}
```

### Important considerations for delegated authorization
#### Important considerations for delegated authorization

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

Expand All @@ -442,6 +442,115 @@ When implementing delegated authorization with Custom Token Exchange, follow the
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>

### Use Case: Support agent accessing a web application on behalf of an end user

Sometimes a support agent needs to reproduce an issue directly in GearUp's web application, acting as the end-user, rather than just calling GearUp's API. Instead of exchanging tokens for an access token, the support tool requests a Session Transfer Token, which it uses to establish a delegated web session for the end-user in GearUp's web application.

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

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), setting `audience` to `urn:YOUR_AUTH0_TENANT_DOMAIN:session_transfer`, with a signed JWT identifying the end-user as `subject_token` and the agent's ID token as `actor_token`.
3. The associated Custom Token Exchange Action validates the subject token, authorizes the delegation, and calls `api.authentication.setActor()` and `api.authentication.setUserByConnection()` to select the connection the target application accepts. Calling `setActor()` is required when requesting a Session Transfer Token; omitting it returns a `400` error.
4. Auth0 issues a Session Transfer Token (`issued_token_type: urn:auth0:params:oauth:token-type:session_transfer_token`) instead of an access token.
5. The support tool redirects the agent's browser to GearUp's web application, passing the Session Transfer Token as a query parameter on GearUp's `initiate_login_uri`.
6. GearUp's web application passes the Session Transfer Token through to its own call to Auth0's `/authorize` endpoint, which validates the token and establishes an ephemeral, time-limited session for the end-user, with the agent recorded as the actor for auditing. Read [Implement Session Delegation](/docs/authenticate/single-sign-on/session-delegation/implement-session-delegation) for the full request/response detail.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
Your web application must explicitly opt in to accept delegated sessions, and several session behaviors differ from a regular login (session lifetime, refresh tokens, MFA, and more). Read [Session Delegation](/docs/authenticate/single-sign-on/session-delegation) to configure your web application and understand these behaviors, limitations, and how to audit delegated sessions.
</Callout>

The following code sample shows how to implement session delegation in the Custom Token Exchange Action. We check the requested `audience` to tell a session delegation request apart from a plain API-access request and apply a distinct authorization policy for each. Then, set the actor and the user. Auth0 decides whether to issue an access token or a Session Transfer Token based on the requested `audience`, not on the Action's logic.

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

// The audience is `urn:YOUR_AUTH0_TENANT_DOMAIN:session_transfer` - a suffix check (not
// equality) because the domain prefix varies per tenant.
const SESSION_TRANSFER_AUDIENCE_SUFFIX = ":session_transfer";

/**
* 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. DETECT whether this request is for a Session Transfer Token rather than
// an access token, so a distinct authorization policy can be applied.
const isSessionDelegation = event.resource_server?.identifier?.endsWith(SESSION_TRANSFER_AUDIENCE_SUFFIX);

// 3. 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 = isSessionDelegation
? await checkSessionDelegationPolicy(actorUser, payload.sub)
: await checkApiDelegationPolicy(actorUser, payload.sub);

if (!isAuthorized) {
api.access.deny("unauthorized_actor", "Agent is not authorized to act on behalf of this user");
return;
}

// 4. SET THE ACTOR — Auth0 issues a Session Transfer Token instead of an
// access token because the requested audience is the session_transfer audience.
api.authentication.setActor({
sub: actorUser.user_id,
sub_profile: "human",
role: "support"
});

// 5. SET THE USER by connection — the Session Transfer Token is scoped to
// this specific connection, so the target application can only redeem it if
// this connection is enabled for it. We trust the calling application to
// embed the intended connection as a custom claim in subject_token (it
// signed that token).
if (!payload.connection) {
api.access.deny("invalid_request", "subject_token missing connection claim");
return;
}
api.authentication.setUserByConnection(payload.connection, { user_id: payload.sub }, {
creationBehavior: "none",
updateBehavior: "none"
});

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

async function checkApiDelegationPolicy(agent, userId) {
// Implement your API-access delegation policy check here.
}

async function checkSessionDelegationPolicy(agent, userId) {
// Implement your session delegation policy check here.
}
};
```

#### Important considerations for session delegation

In addition to the [considerations for delegated authorization](#important-considerations-for-delegated-authorization), keep in mind:

* Detect it is a session delegation request based on the `session_transfer` audience and apply specific authorization policy for that case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Detect it is a session delegation request based on the `session_transfer` audience and apply specific authorization policy for that case.
* Detecting a session delegation request is based on the `session_transfer` audience, which applies a specific authorization policy for that case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more a direct guidance for the developer to do that in their code, so I think it is better in the original text

* Target application reachability depends on the specific connection your Action sets.
* To set the connection, the application calling Custom Token Exchange needs access to the same connections as your target application(s).
* Use separate Auth0 domains for the initiating and target applications to avoid an existing session on a shared domain blocking the delegated one.
* Agents must log out between delegated sessions for different users.

Read [Implement Session Delegation](/docs/authenticate/single-sign-on/session-delegation/implement-session-delegation) for the full end-to-end implementation detail, including these considerations.

## 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
4 changes: 4 additions & 0 deletions main/docs/authenticate/single-sign-on/native-to-web.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ If your web application relies on embedded WebViews or external browsers to deli

By binding Session Transfer Tokens to the specific device through IP address or ASN, session continuity and security is maintained and ensures the authentication context remains secure throughout the transition.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
If you want a support agent or other authorized actor to establish a session **as another user** rather than move a user's own session from native to web, read [Session Delegation](/docs/authenticate/single-sign-on/session-delegation) instead.
</Callout>

## How it works

<Frame>![Native to Web SSO workflow](/docs/images/cdy7uua7fh8z/4gFE2RP5ZCEcukiNF3Gpae/f957034f63c36d5a11f6a64d988c00e9/sequence-diagram.png)</Frame>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Native to Web SSO does not change standard Auth0 [Single Sign-On](/docs/authenti
3. The user is prompted to login when a pre-existing Auth0 session is found and the `session_transfer_token` belongs to a different user. Additionally, the pre-existing Auth0 session is revoked.
4. The user is prompted to log in when a pre-existing Auth0 session is found and the `session_transfer_token` is invalid.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
These behaviors apply to standard Native to Web SSO. A `session_transfer_token` carrying an actor (issued for [Session Delegation](/docs/authenticate/single-sign-on/session-delegation)) behaves differently: if the subject user already has an active session, the attempt is blocked with an error page instead of revoking the existing session. Read [Delegated Session Behavior and Monitoring](/docs/authenticate/single-sign-on/session-delegation/session-delegation-behavior-and-monitoring#session-behavior) for details.
</Callout>

## Sessions and refresh token revocation

A [`session_transfer_token`](/docs/authenticate/single-sign-on/native-to-web/configure-implement-native-to-web#create-and-manage-session-transfer-tokens) is used to initiate a secure session in a WebView or browser to securely authenticate the user without being prompted to login. These web sessions may also issue their own <Tooltip tip="Refresh Token: Token used to obtain a renewed Access Token without forcing users to log in again." cta="View Glossary" href="/docs/glossary?term=refresh+tokens">refresh tokens</Tooltip>.
Expand Down
Loading
Loading