-
Notifications
You must be signed in to change notification settings - Fork 68
Feat/session expiry OIDC ipsie #1379
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
Open
avanscoy
wants to merge
11
commits into
main
Choose a base branch
from
feat/session-expiry-oidc-ipsie
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+331
−1
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b6324d0
First draft
avanscoy f3fe952
docs: update session expiry for enterprise connections content and na…
avanscoy aba5194
Minor changes
avanscoy cf633a7
Updated with Auth0 Actions and requested Legal verbage
avanscoy f46591a
fix: correct idp_session_expiry log example to seconds and update rev…
avanscoy 24152d6
fix: address remaining PR review feedback
avanscoy 7e2b8cb
Updated setting title
avanscoy 54327df
Update main/docs/authenticate/enterprise-connections/session-expiry-e…
avanscoy 71eb5ac
Apply suggestion from @BcnCarlos
avanscoy 2a1d910
Update inaccurate Dashboard instructions
avanscoy 671a69d
Apply suggestions from code review
avanscoy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
323 changes: 323 additions & 0 deletions
323
...s/authenticate/enterprise-connections/session-expiry-enterprise-connections.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,323 @@ | ||
| --- | ||
| title: Configure Session Expiry for Enterprise Connections (IPSIE) | ||
| description: Learn how to enforce upstream Identity Provider session expiry using the IPSIE `session_expiry` claim for Okta and OIDC Enterprise connections. | ||
| validatedOn: 2026-06-25 | ||
| --- | ||
|
|
||
| <Callout icon="file-lines" color="#0EA5E9" iconType="regular"> | ||
|
|
||
| Session Expiry is supported for Okta and OIDC Enterprise connections and is not available for Microsoft Entra ID (Azure AD) connections. Microsoft does not include the `session_expiry` claim in Entra ID Tokens. | ||
|
|
||
| </Callout> | ||
|
|
||
| Auth0 supports the `session_expiry` claim based on the [Interoperability Profile for Secure Identity in the Enterprise (IPSIE) standard](https://openid.net/specs/ipsie-openid-connect-sl1-profile-1_0.html) for Okta and OIDC Enterprise connections. When enabled, Auth0 captures the `session_expiry` (represented in seconds as a Unix timestamp) from the upstream Identity Provider (IdP) and includes it in the ID Token issued to your application. | ||
|
|
||
| Auth0 consumes the `session_expiry` claim and synchronizes the local Auth0 session with the upstream IdP's session lifecycle. This ensures when a user's session expires at the Enterprise IdP, their Auth0 session also terminates. | ||
|
|
||
| <Warning> | ||
|
avanscoy marked this conversation as resolved.
|
||
| The customer is responsible for processing the received `session_expiry` claim and to terminate and manage user sessions in their applications. | ||
| </Warning> | ||
|
avanscoy marked this conversation as resolved.
|
||
|
|
||
|
avanscoy marked this conversation as resolved.
|
||
| <Card title="Before you start"> | ||
|
|
||
| Before enabling session expiry enforcement: | ||
|
|
||
| * You must have an existing [Okta](/docs/authenticate/identity-providers/enterprise-identity-providers/okta/express-configuration) or [OIDC](/docs/authenticate/identity-providers/enterprise-identity-providers/oidc) Enterprise connection. | ||
| * The upstream identity provider must emit a `session_expiry` claim in its ID Token. | ||
|
|
||
| </Card> | ||
|
|
||
| ## How it works | ||
|
|
||
| When a user authenticates through a `session_expiry` enabled Enterprise connection, Auth0: | ||
|
|
||
| 1. Captures the `session_expiry` claim from the upstream IdP's ID Token. | ||
| 2. Calculates session expiration by evaluating specific parameters and sets the final Auth0 session's expiration to the minimum (earliest) value of the following factors: | ||
| * The IdP `session_expiry` claim: The absolute timestamp in an ID Token from the upstream Identity Provider. | ||
| * Your Auth0 tenant's default Absolute Expiration setting: The session lifetime limit you configure in Auth0 Dashboard or Management API. To learn more, read [Configure Session Lifetime Settings](/docs/manage-users/sessions/configure-session-lifetime-settings). | ||
| * Auth0 Actions [`setExpiresAt`]: Any custom expiration timestamp programmatically set during the login transaction with the Post-Login Action [`api.session.setExpiresAt()`](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/post-login-api-object#api-session-setexpiresat-absolute) method. | ||
| 3. Uses a Post-Login Action you configure during enablement to pass the final evaluated session expiration to your application by injecting it as a [custom claim](/docs/customize/actions/explore-triggers/signup-and-login-triggers/login-trigger/post-login-api-object#api-idtoken-setcustomclaim-key-value) into the Auth0-issued ID Token. | ||
|
|
||
| The `session_expiry` claim is a [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time) in seconds representing the absolute expiration limit for the user's session: | ||
|
|
||
| ```json | ||
| { | ||
| "iss": "https://YOUR_DOMAIN.auth0.com/", | ||
| "aud": "YOUR_CLIENT_ID", | ||
| "sub": "oidc|username@domain.com", | ||
| "iat": 1748534400, | ||
| "exp": 1748538000, | ||
| "session_expiry": 1748566800 | ||
| } | ||
| ``` | ||
|
|
||
| | Claim | What it represents | Scope | | ||
| | --- | --- | --- | | ||
| | `exp` | ID Token lifetime (typically minutes) | Token validation | | ||
| | `session_expiry` | Absolute expiration (seconds) | Session management | | ||
|
|
||
| The `session_expiry` claim is not a replacement for `exp`. The ID Token's own `exp` remains short-lived and unchanged. `session_expiry` is a session-level limit included with the token claims. | ||
|
|
||
| **`session_expiry` is fixed at login.** It is set once when the user authenticates and is not updated when tokens are refreshed. A user already logged in before this feature is enabled will not have `session_expiry` on their existing session; the claim only appears after their next login. | ||
|
|
||
| <Callout icon="file-lines" color="#0EA5E9" iconType="regular"> | ||
|
|
||
| This feature addresses scheduled session expiry only. For real-time session revocation, like when a user is off-boarded mid-session, we recommend you use [Back-Channel Logout](/docs/authenticate/login/logout/back-channel-logout). | ||
|
|
||
| </Callout> | ||
|
|
||
| ## Enable session expiry enforcement | ||
|
|
||
| Configure session expiry enforcement on your Enterprise connection using the Auth0 Dashboard or Management API. | ||
|
|
||
| <Tabs> | ||
| <Tab title="Auth0 Dashboard"> | ||
|
|
||
| 1. Navigate to [Auth0 Dashboard > Authentication > Enterprise](https://manage.auth0.com/#/connections/enterprise) in the Auth0 Dashboard. | ||
| 2. Find the Okta or OpenID Connect Enterprise connection and select **Browse**. | ||
| 3. Choose the connection you want to configure. | ||
| 4. Under Settings, enable **Use ID Token for Session Expiry**. | ||
| 5. Select **Save**. | ||
|
|
||
| </Tab> | ||
| <Tab title="Management API"> | ||
|
|
||
| To use the Management API, you need a [Management API access token](/docs/secure/tokens/access-tokens/management-api-access-tokens) with `update:connections` scope. | ||
|
|
||
| Make a `PATCH` request to the [Update a connection](https://auth0.com/docs/api/management/v2/connections/patch-connections-by-id) endpoint: | ||
|
|
||
| ```http | ||
| PATCH https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_ID | ||
| Content-Type: application/json | ||
| Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN | ||
|
|
||
| { | ||
| "options": { | ||
| "id_token_session_expiry_supported": true | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Replace the placeholder values: | ||
|
|
||
| * **`YOUR_DOMAIN`**: Your Auth0 tenant domain. Example: `travel0.us.auth0.com`. | ||
| * **`YOUR_CONNECTION_ID`**: The ID of your Okta or OIDC Enterprise connection. | ||
| * **`YOUR_MANAGEMENT_API_TOKEN`**: A Management API token with `update:connections` scope. | ||
|
|
||
| <Callout icon="file-lines" color="#0EA5E9" iconType="regular"> | ||
|
|
||
| If you `PATCH` the `options` parameter, the entire `options` object is overridden. Ensure all existing properties are included when you `PATCH` `options`. | ||
|
|
||
| </Callout> | ||
|
|
||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| ## Send session expiration to your application | ||
|
|
||
| While Auth0 uses the calculated session expiration to manage its own session layer, your downstream applications may also need to know this absolute expiration time to enforce local session limits. Configure a Post-Login Action to inject the final, evaluated session expiration in an Auth0-issued ID Token and pass the token to your application. | ||
|
|
||
| <Tabs> | ||
| <Tab title="Auth0 Dashboard"> | ||
|
|
||
| 1. Navigate to [**Auth0 Dashboard > Actions > Library**](https://manage.auth0.com/#/actions/library) in the Auth0 Dashboard and select **Build Custom Action**. | ||
| 2. Enter a name for the Action, select **Login / Post Login** as the trigger, and select **Create**. | ||
| 3. Add the following code to your Action: | ||
|
|
||
| ```javascript | ||
| exports.onExecutePostLogin = async (event, api) => { | ||
| // Check if a session expiration has been established | ||
| if (event.session?.expires_at) { | ||
| // Convert the ISO string date to a Date object | ||
| const exp_date = new Date(event.session.expires_at); | ||
| // Set the session_expiry custom claim as a Unix timestamp (seconds) | ||
| api.idToken.setCustomClaim('session_expiry', Math.floor((exp_date.getTime()) / 1000)); | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| 4. Under the Test panel, select **Run** and review results. | ||
| 5. Select **Deploy**. | ||
| 6. Navigate to [**Actions > Triggers**](https://manage.auth0.com/#/actions/triggers) and select **Post-Login**. | ||
| 7. Locate your Action and drag it into the Login flow. Select **Apply**. | ||
|
|
||
| </Tab> | ||
| <Tab title="Management API"> | ||
|
|
||
| Configuring the Action via the Management API requires three steps: create the Action, deploy it, then bind it to the Login trigger. You need a [Management API access token](/docs/secure/tokens/access-tokens/management-api-access-tokens) with `create:actions`, `read:actions`, and `update:actions` scopes. | ||
|
|
||
| ### Create the Action | ||
|
|
||
| 1. Make a `POST` request to the [Create an action](https://auth0.com/docs/api/management/v2/actions/post-action) endpoint: | ||
|
|
||
| ```http | ||
| POST https://YOUR_DOMAIN/api/v2/actions/actions | ||
| Content-Type: application/json | ||
| Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN | ||
|
|
||
| { | ||
| "name": "Set session_expiry claim", | ||
| "supported_triggers": [ | ||
| { "id": "post-login" } | ||
| ], | ||
| "code": "exports.onExecutePostLogin = async (event, api) => {\n if (event.session?.expires_at) {\n const exp_date = new Date(event.session.expires_at);\n api.idToken.setCustomClaim('session_expiry', Math.floor((exp_date.getTime()) / 1000));\n }\n};" | ||
| } | ||
| ``` | ||
|
|
||
| Note the `id` value in the response since you need it in the following steps. | ||
|
|
||
| ### Deploy the Action | ||
|
|
||
| 1. Make a `POST` request to the [Deploy an action](https://auth0.com/docs/api/management/v2/actions/post-deploy-action) endpoint: | ||
|
|
||
| ```http | ||
| POST https://YOUR_DOMAIN/api/v2/actions/actions/YOUR_ACTION_ID/deploy | ||
| Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN | ||
| ``` | ||
|
|
||
| ### Bind the Action to the Login trigger | ||
|
|
||
| 1. Make a `PATCH` request to the [Update trigger bindings](https://auth0.com/docs/api/management/v2/actions/patch-bindings) endpoint: | ||
|
|
||
| ```http | ||
| PATCH https://YOUR_DOMAIN/api/v2/actions/triggers/post-login/bindings | ||
| Content-Type: application/json | ||
| Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN | ||
|
|
||
| { | ||
| "bindings": [ | ||
| { | ||
| "ref": { | ||
| "type": "action_id", | ||
| "value": "YOUR_ACTION_ID" | ||
| }, | ||
| "display_name": "Set session_expiry claim" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| <Callout icon="triangle-exclamation" color="#F59E0B" iconType="regular"> | ||
|
|
||
| The `PATCH /api/v2/actions/triggers/post-login/bindings` request replaces all existing bindings. To preserve existing Actions in your Login flow, first retrieve your current bindings with `GET /api/v2/actions/triggers/post-login/bindings`, then include those bindings alongside the new one in your `PATCH` request. | ||
|
|
||
| </Callout> | ||
|
|
||
| Replace the placeholder values: | ||
|
|
||
| * **`YOUR_DOMAIN`**: Your Auth0 tenant domain. Example: `travel0.us.auth0.com`. | ||
| * **`YOUR_MANAGEMENT_API_TOKEN`**: A Management API token with `create:actions`, `read:actions`, and `update:actions` scopes. | ||
| * **`YOUR_ACTION_ID`**: The `id` returned in the Create Action response. | ||
|
|
||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| ## Use session expiry with Auth0 SDKs | ||
|
|
||
| If you use [Auth0 SDKs](/docs/libraries) and have configured the Post-Login Action described above, session expiry is enforced automatically. The SDK reads `session_expiry` from the ID Token at login, persists it with the session, and treats the session as expired once the current time reaches or passes `session_expiry`: | ||
|
|
||
| | SDK type | How expiry is enforced | | ||
| | --- | --- | | ||
| | **Regular Web App** (Next.js, Express, Python) | Middleware clears the session and redirects with `prompt=login`; `getAccessToken()` throws `SessionExpiredError` | | ||
| | **Single-Page App** (React, Angular, Vue) | `getTokenSilently()` / `getAccessTokenSilently()` rejects and triggers re-login with `prompt=login` | | ||
| | **Mobile** (iOS/Swift, Android/Kotlin) | `CredentialsManager.credentials()` returns `noCredentials` and the app's existing login path handles re-authentication | | ||
|
|
||
| The Post-Login Action is required to inject the `session_expiry` claim into the ID Token. Once set, when a session expires, the SDK behaves the same as any other session expiry: the user is redirected to log in. No additional error handling is required. | ||
|
|
||
| ## Add session expiry values to your application | ||
|
|
||
| An optional step is to add session expiration values to your application. For example, your application can read `session_expiry` to show users a session-expiring warning or you can bind your application's own session lifetime to the upstream IdP's value. | ||
|
|
||
| <Tabs> | ||
| <Tab title="Single-Page App"> | ||
|
|
||
| ```javascript | ||
| const claims = await auth0.getIdTokenClaims(); | ||
| const sessionExpiresAt = claims?.session_expiry; // Unix seconds | ||
| const remainingSeconds = sessionExpiresAt - Math.floor(Date.now() / 1000); | ||
| ``` | ||
|
|
||
| </Tab> | ||
| <Tab title="Regular Web App"> | ||
|
|
||
| ```javascript | ||
| const session = await auth0.getSession(); | ||
| const sessionExpiresAt = session?.sessionExpiresAt; // top-level field, Unix seconds | ||
| const remainingSeconds = (sessionExpiresAt ?? Infinity) - Math.floor(Date.now() / 1000); | ||
| ``` | ||
|
|
||
| </Tab> | ||
| <Tab title="Mobile (Swift)"> | ||
|
|
||
| ```swift | ||
| credentialsManager.credentials { result in | ||
| switch result { | ||
| case .success(let credentials): | ||
| let sessionExpiresAt = credentials.idToken?.session_expiry // Unix seconds | ||
| case .failure: | ||
| startLogin() | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| Do not persist the `session_expiry` value in a long-lived store, such as a cookie or `localStorage`, without re-validating it on each read. The value is only meaningful relative to the current wall-clock time. | ||
|
|
||
| ## Verify in tenant logs | ||
|
|
||
| After enabling session expiry enforcement, verify session expiry is working by checking [tenant logs](/docs/deploy-monitor/logs). | ||
|
|
||
| Navigate to [**Auth0 Dashboard > Monitoring > Logs**](https://manage.auth0.com/#/logs) and look for a successful login (`s`) event for a user authenticating through the configured Enterprise connection. When the upstream IdP's `session_expiry` is less than or equal to your tenant's configured absolute session lifetime, the log entry includes the `idp_session_expiry` field (a Unix timestamp in seconds): | ||
|
|
||
| ```json | ||
| { | ||
| "type": "s", | ||
| "description": "Success Login", | ||
| "details": { | ||
| "idp_session_expiry": 1782472241 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| If the IdP did not send a `session_expiry` claim and you enable the feature, login fails with an error message: `The upstream Identity Provider did not return a session_expiry claim`. | ||
|
|
||
| ## Disable session expiry enforcement | ||
|
|
||
| <Tabs> | ||
| <Tab title="Auth0 Dashboard"> | ||
|
|
||
| 1. Navigate to [**Authentication > Enterprise**](https://manage.auth0.com/#/connections/enterprise) in the Auth0 Dashboard. | ||
| 2. Select the connection you want to configure. | ||
| 3. Select the **Settings** tab. | ||
| 4. Disable **Use ID Token for Session Expiry**. | ||
| 5. Select **Save**. | ||
|
|
||
| </Tab> | ||
| <Tab title="Management API"> | ||
|
|
||
| Set `id_token_session_expiry_supported` to `false` in your connection options: | ||
|
|
||
| ```http | ||
| PATCH https://YOUR_DOMAIN/api/v2/connections/YOUR_CONNECTION_ID | ||
| Content-Type: application/json | ||
| Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN | ||
|
|
||
| { | ||
| "options": { | ||
| "id_token_session_expiry_supported": false | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| ## Learn more | ||
|
|
||
| * [Session Lifecycle](/docs/manage-users/sessions/session-lifecycle) | ||
| * [Configure Session Lifetime](/docs/manage-users/sessions/configure-session-lifetime) | ||
| * [Sessions with Actions](/docs/manage-users/sessions/manage-sessions-actions) | ||
| * [Back-Channel Logout](/docs/authenticate/login/logout/back-channel-logout) | ||
| * [IPSIE SL1 OpenID Connect Profile](https://openid.net/specs/ipsie-openid-connect-sl1-profile-1_0.html) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.