|
3 | 3 | - [Logging Out](#logging-out) |
4 | 4 | - [Calling an API](#calling-an-api) |
5 | 5 | - [Refresh Tokens](#refresh-tokens) |
| 6 | +- [Online Access (Online Refresh Tokens)](#online-access-online-refresh-tokens) |
6 | 7 | - [Data Caching Options](#creating-a-custom-cache) |
7 | 8 | - [Organizations](#organizations) |
8 | 9 | - [Native to Web SSO](#native-to-web-sso) |
@@ -153,6 +154,9 @@ The `revokeRefreshToken()` method explicitly revokes a refresh token via the `/o |
153 | 154 |
|
154 | 155 | This method only has an effect when `useRefreshTokens` is `true`. If refresh tokens are disabled it returns immediately without doing anything. |
155 | 156 |
|
| 157 | +> [!NOTE] |
| 158 | +> In [online access](#online-access-online-refresh-tokens) mode (`refreshTokenMode: RefreshTokenMode.Online`), `revokeRefreshToken()` only clears the cached refresh token locally — it does **not** revoke the Online Refresh Token at the authorization server. Online Refresh Tokens are non-rotating and session-bound, and the server does not support token-only revocation for them. To end an online session, call `logout()` instead, which terminates the session and thereby invalidates the ORT. |
| 159 | +
|
156 | 160 | ```js |
157 | 161 | // Revoke the refresh token for the default audience |
158 | 162 | await auth0.revokeRefreshToken(); |
@@ -205,6 +209,141 @@ When using [Multi-Resource Refresh Tokens (MRRT)](#using-multi-resource-refresh- |
205 | 209 | await auth0.revokeRefreshToken(); |
206 | 210 | ``` |
207 | 211 |
|
| 212 | +## Online Access (Online Refresh Tokens) |
| 213 | + |
| 214 | +**Online Refresh Tokens (ORTs)** are a refresh token type bound to the lifetime of the user's Auth0 session. See the [Auth0 documentation on Online Refresh Tokens](https://auth0.com/docs/secure/tokens/refresh-tokens/online-refresh-tokens/online-refresh-tokens) for the full conceptual overview. Unlike the rotating [offline refresh tokens](#refresh-tokens) described above, an ORT is: |
| 215 | + |
| 216 | +- **Session-bound** — it is valid only while the underlying Auth0 session is active. When the session ends (logout, idle/absolute session expiry, or an admin revoking the session), the ORT stops working. |
| 217 | +- **Non-rotating** — refreshing an access token with an ORT does **not** issue a new refresh token. The same ORT is reused for the life of the session. |
| 218 | + |
| 219 | +This makes ORTs a good fit for SPAs that want a refresh-token renewal path whose lifetime tracks the SSO session rather than living independently of it. |
| 220 | + |
| 221 | +> [!IMPORTANT] |
| 222 | +> Online access requires DPoP. Sender-constraining the token via [DPoP](#device-bound-tokens-with-dpop) is mandatory because the ORT is non-rotating — binding it to the browser's key pair is what mitigates token replay if it is exfiltrated. You must set `useDpop: true` explicitly; the SDK does not enable it for you. |
| 223 | +
|
| 224 | +### Enabling Online Access |
| 225 | + |
| 226 | +Set `refreshTokenMode` to `RefreshTokenMode.Online` together with `useRefreshTokens: true` and `useDpop: true`: |
| 227 | + |
| 228 | +```js |
| 229 | +import { createAuth0Client, RefreshTokenMode } from '@auth0/auth0-spa-js'; |
| 230 | + |
| 231 | +const auth0 = await createAuth0Client({ |
| 232 | + domain: '<AUTH0_DOMAIN>', |
| 233 | + clientId: '<AUTH0_CLIENT_ID>', |
| 234 | + useRefreshTokens: true, // required — online access is a refresh-token grant |
| 235 | + refreshTokenMode: RefreshTokenMode.Online, |
| 236 | + useDpop: true, // required — DPoP is mandatory for online access |
| 237 | + authorizationParams: { |
| 238 | + redirect_uri: '<MY_CALLBACK_URL>' |
| 239 | + } |
| 240 | +}); |
| 241 | +``` |
| 242 | + |
| 243 | +`refreshTokenMode` is a sub-option of `useRefreshTokens`. It defaults to `RefreshTokenMode.Offline` (the rotating [offline refresh tokens](#refresh-tokens) described above); setting it to `RefreshTokenMode.Online` opts into Online Refresh Tokens. Always reference the exported `RefreshTokenMode` enum rather than hard-coding the mode. |
| 244 | + |
| 245 | +Enabling this option causes the SDK to: |
| 246 | + |
| 247 | +- Send the `online_access` scope to the authorization server (instead of `offline_access`). You do **not** need to add it to `authorizationParams.scope` yourself — the SDK injects it. |
| 248 | +- Route token renewal through the `refresh_token` grant against `/oauth/token` (the same path used by offline refresh tokens), rather than a hidden iframe. |
| 249 | +- Store the non-rotating ORT in the existing cache and reuse it on every refresh, never replacing it. |
| 250 | + |
| 251 | +> [!NOTE] |
| 252 | +> Online access is **opt-in**. When `refreshTokenMode` is unset or `RefreshTokenMode.Offline`, the SDK behaves exactly as before. |
| 253 | +
|
| 254 | +> [!NOTE] |
| 255 | +> This feature requires the `online_refresh_tokens` flag to be enabled for your tenant and `allow_online_access` to be enabled on the resource server (on by default). |
| 256 | +
|
| 257 | +### `RefreshTokenMode.Offline` vs. `RefreshTokenMode.Online` |
| 258 | + |
| 259 | +`refreshTokenMode` selects which refresh-token type the refresh-token grant uses. It is a sub-option of `useRefreshTokens` (which must be `true` for either mode) and defaults to `RefreshTokenMode.Offline`: |
| 260 | + |
| 261 | +| | `RefreshTokenMode.Offline` (default) | `RefreshTokenMode.Online` | |
| 262 | +| --- | --- | --- | |
| 263 | +| Requires | `useRefreshTokens: true` | `useRefreshTokens: true` + `useDpop: true` | |
| 264 | +| Scope injected | `offline_access` | `online_access` | |
| 265 | +| Token lifetime | Independent of the session (survives logout until revoked/expired) | Bound to the Auth0 session | |
| 266 | +| Rotation | Rotating (a new RT is issued on each refresh) | Non-rotating (same RT reused) | |
| 267 | +| DPoP | Optional | **Required** (`useDpop: true`) | |
| 268 | + |
| 269 | +The two modes inject mutually exclusive scopes (`offline_access` vs. `online_access`), so the SDK emits only one — it never sends both. You select between them with `refreshTokenMode`, not by combining flags. |
| 270 | + |
| 271 | +### Configuration validation |
| 272 | + |
| 273 | +The SDK enforces the DPoP requirement at two layers: |
| 274 | + |
| 275 | +1. **Compile-time (TypeScript).** When you call `createAuth0Client` with `refreshTokenMode: RefreshTokenMode.Online`, the compiler requires both `useRefreshTokens: true` and `useDpop: true`: |
| 276 | + |
| 277 | + ```ts |
| 278 | + import { createAuth0Client, RefreshTokenMode } from '@auth0/auth0-spa-js'; |
| 279 | + |
| 280 | + // ❌ compile error: `useRefreshTokens: true` and `useDpop: true` are required for online mode |
| 281 | + createAuth0Client({ domain, clientId, refreshTokenMode: RefreshTokenMode.Online }); |
| 282 | + |
| 283 | + // ❌ compile error: `useDpop: true` is still required |
| 284 | + createAuth0Client({ domain, clientId, refreshTokenMode: RefreshTokenMode.Online, useRefreshTokens: true }); |
| 285 | + |
| 286 | + // ✅ valid |
| 287 | + createAuth0Client({ domain, clientId, refreshTokenMode: RefreshTokenMode.Online, useRefreshTokens: true, useDpop: true }); |
| 288 | + ``` |
| 289 | + |
| 290 | + > [!NOTE] |
| 291 | + > The compile-time check narrows on the online mode value. A dynamically-typed value (e.g. a `refreshTokenMode` read from config at runtime), an `as any` cast, or plain JavaScript all bypass it — which is why the runtime check below exists too. |
| 292 | +
|
| 293 | +2. **Runtime (all consumers, including plain JS).** The `Auth0Client` constructor throws an `InvalidConfigurationError` when online mode is requested but `useRefreshTokens` or `useDpop` is not `true`. The error's `suggestion` tells you exactly which option to set: |
| 294 | + |
| 295 | + ```js |
| 296 | + import { createAuth0Client, RefreshTokenMode, InvalidConfigurationError } from '@auth0/auth0-spa-js'; |
| 297 | + |
| 298 | + try { |
| 299 | + const auth0 = await createAuth0Client({ |
| 300 | + domain: '<AUTH0_DOMAIN>', |
| 301 | + clientId: '<AUTH0_CLIENT_ID>', |
| 302 | + refreshTokenMode: RefreshTokenMode.Online, |
| 303 | + useRefreshTokens: true // missing useDpop: true |
| 304 | + }); |
| 305 | + } catch (e) { |
| 306 | + if (e instanceof InvalidConfigurationError) { |
| 307 | + console.error(e.error_description); // includes the suggested fix |
| 308 | + console.error(e.suggestion); // 'Set `useDpop: true` (DPoP is mandatory for online access).' |
| 309 | + } |
| 310 | + } |
| 311 | + ``` |
| 312 | + |
| 313 | +### Logging out |
| 314 | + |
| 315 | +Because an ORT is bound to the Auth0 session, the way to invalidate it is to end the session with `logout()`, which clears the local cache and redirects to `/v2/logout`: |
| 316 | + |
| 317 | +```js |
| 318 | +await auth0.logout({ logoutParams: { returnTo: window.location.origin } }); |
| 319 | +``` |
| 320 | + |
| 321 | +After logout, the ORT is no longer valid; a subsequent `getTokenSilently()` falls through to the [iframe fallback](#refresh-token-fallback) (if `useRefreshTokensFallback` is enabled) and ultimately to an interactive login. |
| 322 | + |
| 323 | +> [!NOTE] |
| 324 | +> In online mode, [`revokeRefreshToken()`](#revoking-refresh-tokens) only clears the cached token locally — it does **not** revoke the ORT at the authorization server. The server has no token-only revocation for non-rotating ORTs, so `logout()` is the only way to invalidate an ORT. |
| 325 | +
|
| 326 | +### Using with Multi-Resource Refresh Tokens (MRRT) |
| 327 | + |
| 328 | +Online access is compatible with [MRRT](#using-multi-resource-refresh-tokens): a single ORT can be exchanged for access tokens across the audiences allowed by your refresh-token policies. The ORT remains non-rotating throughout — the same token is reused for every cross-audience exchange. |
| 329 | + |
| 330 | +```js |
| 331 | +import { createAuth0Client, RefreshTokenMode } from '@auth0/auth0-spa-js'; |
| 332 | + |
| 333 | +const auth0 = await createAuth0Client({ |
| 334 | + domain: '<AUTH0_DOMAIN>', |
| 335 | + clientId: '<AUTH0_CLIENT_ID>', |
| 336 | + useRefreshTokens: true, |
| 337 | + refreshTokenMode: RefreshTokenMode.Online, |
| 338 | + useDpop: true, |
| 339 | + useMrrt: true, |
| 340 | + authorizationParams: { |
| 341 | + redirect_uri: '<MY_CALLBACK_URL>', |
| 342 | + audience: 'https://api.example.com' |
| 343 | + } |
| 344 | +}); |
| 345 | +``` |
| 346 | + |
208 | 347 | ## Data caching options |
209 | 348 |
|
210 | 349 | The SDK can be configured to cache ID tokens and access tokens either in memory or in local storage. The default is in memory. This setting can be controlled using the `cacheLocation` option when creating the Auth0 client. |
|
0 commit comments