-
Notifications
You must be signed in to change notification settings - Fork 71
feat: add Session Delegation docs #1624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
8ee2415
27ad2b9
c15e271
ccef07f
9297d20
349423a
6e29142
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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](https://auth0.com/docs/ja-jp/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 `actor_token` is present, refresh tokens are not issued. The `offline_access` scope is excluded from the response. | ||||||
| - 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. | ||||||
|
dlozlla marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| - 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.` | ||||||
| - To request a Session Transfer Token instead of an access token, set `audience` to `urn:{yourDomain}: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. | ||||||
|
|
||||||
| - 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. Read [OpenID Connect Scopes: Standard claims](/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims) to learn more. | ||||||
|
dlozlla marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| ## Headers | ||||||
| <ParamField header="DPoP" type="string"> | ||||||
|
|
@@ -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:{yourDomain}:session_transfer`. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| </ParamField> | ||||||
|
|
||||||
| <ParamField body="resource" type="string"> | ||||||
|
|
@@ -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"> | ||||||
|
|
@@ -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), if `setActor()` was not called when requesting a Session Transfer Token, or if Session Transfer Tokens are requested while the `cte_session_transfer_token` feature is disabled. | | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. Reach for 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. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| </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 | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. | ||||||
|
|
||||||
|
|
@@ -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: | ||||||
|
|
||||||
|
|
@@ -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></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:{yourDomain}: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_url`. | ||||||
| 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 exemplifies how to implement this 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 anything the Action does differently. | ||||||
|
dlozlla marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| ```javascript lines expandable | ||||||
| const jwksUri = "https://gearup.com/.well-known/jwks.json"; | ||||||
|
|
||||||
| // The audience is `urn:{yourDomain}: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) above, keep in mind: | ||||||
|
dlozlla marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| * Detect it is a session delegation request based on the `session_transfer` audience and apply specific authorization policy for that case. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
|
dlozlla marked this conversation as resolved.
Outdated
|
||||||
| * 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. | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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"> | ||||||
| Looking to let a support agent or other authorized actor establish a session **as another user**, rather than moving a user's own session from native to web? Read [Session Delegation](/docs/authenticate/single-sign-on/session-delegation) instead. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| </Callout> | ||||||
|
|
||||||
| ## How it works | ||||||
|
|
||||||
| <Frame></Frame> | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.