|
18 | 18 | - [Native to Web SSO](#native-to-web-sso) |
19 | 19 | - [Passkeys](#passkeys) |
20 | 20 | - [MyAccount API](#myaccount-api) |
| 21 | +- [Session Expiry from Upstream IdP (IPSIE)](#session-expiry-from-upstream-idp-ipsie) |
21 | 22 |
|
22 | 23 | ## Use with a Class Component |
23 | 24 |
|
@@ -1616,3 +1617,81 @@ try { |
1616 | 1617 | } |
1617 | 1618 | } |
1618 | 1619 | ``` |
| 1620 | +
|
| 1621 | +## Session Expiry from Upstream IdP (IPSIE) |
| 1622 | +
|
| 1623 | +When using an Okta or OIDC enterprise connection configured with `id_token_session_expiry_supported: true`, Auth0 includes a `session_expiry` claim in the ID token. This is an absolute Unix timestamp (seconds) that acts as a hard ceiling on the local session — the SDK will not return tokens or a user once this point in time is reached. |
| 1624 | +
|
| 1625 | +You can also emit the claim from a Post-Login Action: |
| 1626 | +
|
| 1627 | +```js |
| 1628 | +exports.onExecutePostLogin = async (event, api) => { |
| 1629 | + // Value must be Unix seconds, not milliseconds. |
| 1630 | + api.idToken.setCustomClaim('session_expiry', Math.floor(Date.now() / 1000) + 7200); // 2-hour ceiling |
| 1631 | +}; |
| 1632 | +``` |
| 1633 | +
|
| 1634 | +### Behavior |
| 1635 | +
|
| 1636 | +Enforcement is transparent — no code changes are required. When the ceiling is reached, `useAuth0()` reflects the expired state on the next render: |
| 1637 | +
|
| 1638 | +- `isAuthenticated` becomes `false` |
| 1639 | +- `user` becomes `undefined` |
| 1640 | +- `getAccessTokenSilently()` returns `undefined` (no error thrown) |
| 1641 | +
|
| 1642 | +Because this is identical to "no session", any route guard or `withAuthenticationRequired` wrapper already in your app will trigger re-authentication automatically. |
| 1643 | +
|
| 1644 | +```jsx |
| 1645 | +// This component already handles the session_expiry ceiling with no changes. |
| 1646 | +// When the ceiling passes, isAuthenticated becomes false and the HOC redirects to login. |
| 1647 | +export default withAuthenticationRequired(Dashboard); |
| 1648 | +``` |
| 1649 | +
|
| 1650 | +### Reading the claim |
| 1651 | +
|
| 1652 | +`session_expiry` is a standard ID token claim and is available via `getIdTokenClaims()`: |
| 1653 | +
|
| 1654 | +```jsx |
| 1655 | +import { useAuth0 } from '@auth0/auth0-react'; |
| 1656 | + |
| 1657 | +function SessionInfo() { |
| 1658 | + const { getIdTokenClaims } = useAuth0(); |
| 1659 | + |
| 1660 | + useEffect(() => { |
| 1661 | + getIdTokenClaims().then((claims) => { |
| 1662 | + if (claims?.session_expiry) { |
| 1663 | + const ceiling = new Date(claims.session_expiry * 1000); |
| 1664 | + console.log('Session ceiling:', ceiling.toISOString()); |
| 1665 | + } |
| 1666 | + }); |
| 1667 | + }, [getIdTokenClaims]); |
| 1668 | + |
| 1669 | + return null; |
| 1670 | +} |
| 1671 | +``` |
| 1672 | +
|
| 1673 | +### Upgrading existing apps |
| 1674 | +
|
| 1675 | +Once the feature is enabled, `user` and `getAccessTokenSilently()` can return `undefined` for a previously authenticated user when the ceiling is reached. Apps that assume these are always set after login should add null checks: |
| 1676 | +
|
| 1677 | +```jsx |
| 1678 | +function CallApi() { |
| 1679 | + const { getAccessTokenSilently, loginWithRedirect } = useAuth0(); |
| 1680 | + |
| 1681 | + async function fetchData() { |
| 1682 | + const token = await getAccessTokenSilently(); |
| 1683 | + |
| 1684 | + if (!token) { |
| 1685 | + // Ceiling was reached — redirect to login. |
| 1686 | + await loginWithRedirect(); |
| 1687 | + return; |
| 1688 | + } |
| 1689 | + |
| 1690 | + await fetch('/api/data', { headers: { Authorization: `Bearer ${token}` } }); |
| 1691 | + } |
| 1692 | + |
| 1693 | + return <button onClick={fetchData}>Fetch</button>; |
| 1694 | +} |
| 1695 | +``` |
| 1696 | +
|
| 1697 | +Using `withAuthenticationRequired` on protected routes is the simpler alternative — the redirect happens automatically without the null check. |
0 commit comments