Auth0 SPA JS SDK

repository·main·Indexed 21 days ago

https://github.com/auth0/auth0-spa-js

A client-side library for Single Page Applications to implement secure authentication and authorization using the Authorization Code Grant Flow with PKCE. The SDK supports features such as silent authentication, Rotating Refresh Tokens, Multi-Resource Refresh Tokens (MRRT), and Online Refresh Tokens (ORTs) with DPoP.

Tokens
33.4K
Snippets
109
Records
143
Agent score
75%

What's inside @auth0/auth0-spa-js

  1. Prevent cross-tab token-refresh races

    main
    To prevent multiple browser tabs from triggering concurrent token refreshes for the same key (which can cause token-rotation races), the SDK uses a locking mechanism via getLockManager() and runWithLock(). This is specifically utilized during getTokenSilently. If you are refactoring the token refresh path, you must preserve this locking mechanism to maintain cross-tab safety.
  2. Behavior of revokeRefreshToken() in Offline vs Online mode

    main

    The impact of revokeRefreshToken() depends on your refreshTokenMode:

    • Offline Mode (Default): Only the refresh token entry is cleared from the cache. The access token remains valid until it expires. The Auth0 session is not terminated.
    • Online Mode (RefreshTokenMode.Online): The Auth0 session is terminated server-side. The entire local cache (access token, ID token, and user profile) is wiped immediately. isAuthenticated() returns false and getUser() returns undefined right away.

    If you want a redirect-based sign-out that clears everything, use logout() instead.

  3. Use customTokenExchange() for Delegation and Impersonation

    main

    Use customTokenExchange() when one principal (the actor) needs to act on behalf of another (the subject), such as an AI agent acting for a user.

    Crucial Difference: Unlike loginWithCustomTokenExchange(), this method has no side effects on the SDK's internal state. It does not update the current session, and isAuthenticated() or getUser() will remain unchanged. It simply returns a tokenResponse containing an access_token for use in downstream API calls.

    const tokenResponse = await auth0.customTokenExchange({
      subject_token: '<USER_TOKEN>',
      subject_token_type: 'urn:acme:user-token',
      actor_token: '<AGENT_TOKEN>',
      actor_token_type: 'https://idp.example.com/token-type/agent',
      audience: 'https://api.example.com'
    });
    
    // Use tokenResponse.access_token to call a downstream API
    // The current user session is unchanged
  4. How Refresh Token fallback works

    main

    If a refresh token is unavailable (e.g., the page was refreshed and an in-memory cache was used), the SDK falls back to using a hidden iframe with prompt=none to attempt silent authentication.

    If this fallback fails, the SDK throws a login_required error. Note that this fallback mechanism still requires access to the Auth0 session cookie; if third-party cookies are blocked, the user must re-authenticate manually.

  5. How Multi-Factor Authentication (MFA) works in the SDK

    main

    The SDK manages MFA by intercepting authentication failures and providing a specialized mfa API. When an authentication attempt requires MFA, the SDK throws an MfaRequiredError containing an mfa_token.

    There are two primary flows based on the mfa_requirements object in the error response:

    1. Challenge Flow (mfa_requirements.challenge): The user has already enrolled authenticators. You should use getAuthenticators() $\rightarrow$ challenge() $\rightarrow$ verify().
    2. Enroll Flow (mfa_requirements.enroll): The user needs to set up MFA. You should use getEnrollmentFactors() $\rightarrow$ enroll() $\rightarrow$ verify().

    The SDK automatically handles the context, so you can call MFA methods using the mfa_token from the error.

    NOTE

    MFA support is currently in Early Access. Contact your Auth0 representative to request access.

    // Example of detecting MFA requirement
    try {
      await auth0.getTokenSilently();
    } catch (error) {
      if (error instanceof MfaRequiredError) {
        const mfaToken = error.mfa_token;
        // Proceed to either enrollment or challenge flow
      }
    }
  6. Configure Native to Web SSO

    main

    Native to Web SSO allows users transitioning from a native mobile app to a web app to maintain their session. The SDK can automatically detect a session transfer token in the URL query parameters.

    Automatic Detection

    1. Configure sessionTransferTokenQueryParamName in createAuth0Client with the name of the query parameter used by your native app (e.g., 'session_transfer_token').
    2. When the web app is loaded with that parameter in the URL, the SDK automatically extracts it and includes it in the subsequent /authorize request via loginWithRedirect() or loginWithPopup().
    3. Cleanup: The SDK automatically removes the token from the URL using window.history.replaceState() after extraction to prevent reuse.

    Manual Provisioning

    You can override automatic detection by manually providing the session_transfer_token in authorizationParams. Note that when providing it manually, the SDK will not automatically clean the URL.

    // 1. Configure detection
    const auth0 = await createAuth0Client({
      domain: '<AUTH0_DOMAIN>',
      clientId: '<AUTH0_CLIENT_ID>',
      sessionTransferTokenQueryParamName: 'session_transfer_token',
      authorizationParams: {
        redirect_uri: '<MY_CALLBACK_URL>'
      }
    });
    
    // 2. Use automatically (token is extracted from URL and cleaned)
    await auth0.loginWithRedirect();
    
    // OR: Manual override (URL is NOT cleaned)
    const params = new URLSearchParams(window.location.search);
    const sessionTransferToken = params.get('my_custom_param');
    
    if (sessionTransferToken) {
      await auth0.loginWithRedirect({
        authorizationParams: {
          session_transfer_token: sessionTransferToken
        }
      });
    }
  7. Requirement: Use Refresh Tokens with Passkeys

    main

    When using passkeys, you must configure the SDK with useRefreshTokens: true.

    Why? Passkey authentication uses a direct token exchange (/oauth/token) and does not create an Auth0 session cookie. Without refresh tokens, getTokenSilently() cannot perform a silent refresh via an iframe. This leads to login_required errors or unintended session swaps if a separate Auth0 session cookie exists from a different login method.

    const auth0 = await createAuth0Client({
      domain: '<AUTH0_DOMAIN>',
      clientId: '<AUTH0_CLIENT_ID>',
      useRefreshTokens: true, // Required for passkey-based sessions
      authorizationParams: {
        redirect_uri: '<MY_CALLBACK_URL>'
      }
    });
  8. Understand the transport layer and `switchFetch()`

    main

    Low-level network operations, including timeouts, retries, and DPoP header injection, are managed in src/http.ts. The core function is switchFetch(), which determines the execution path:

    1. Web Worker Path: Used when the worker is active (e.g., refresh-token + in-memory-cache mode) to keep tokens off the main thread.
    2. Main Thread Path: Runs inline when the worker is not in use.

    When debugging transport, timeouts, or DPoP issues, identify which path is active, as bugs may manifest in one path but not the other.

  9. Handle Session Expiry from Upstream IdP (IPSIE)

    main

    If your upstream Identity Provider (like Okta) supports session expiry, Auth0 can include a session_expiry claim in the ID token. This claim is a Unix timestamp in seconds.

    Behavior: When the current time reaches the session_expiry timestamp (with a 30-second clock-skew tolerance), the SDK automatically tears down the local session.

    Important:

    • getUser(), getTokenSilently(), getIdTokenClaims(), and isAuthenticated() will all return undefined or false once the ceiling is reached.
    • Silent token refreshes cannot extend this session.
    • You must add null checks to your application code to handle the case where a user's session has expired upstream.
    // Graceful handling of expired upstream sessions
    const token = await auth0.getTokenSilently();
    
    if (!token) {
      // Session expired upstream, redirect to login
      await auth0.loginWithRedirect();
      return;
    }
    
    fetch('/api/data', {
      headers: { Authorization: `Bearer ${token}` }
    });
  10. Polyfills and Browser Support in v2

    main

    Auth0-SPA-JS v2 no longer supports Internet Explorer 11 (IE11). The SDK bundle is now set to ES2017 and no longer includes internal polyfills. If your application must support older browsers that require these features, you must include them manually in your own application bundle.

    Removed polyfills that you may need to provide:

    • AbortController
    • Promise
    • Core-js (specifically string/startsWith, string/includes, set, symbol, array/from, array/includes)
    • fast-text-encoding (for TextEncoder and TextDecoder)
    • unfetch (for fetch)
  11. Code style and conventions for auth0-spa-js

    main

    When contributing to or reviewing code for auth0-spa-js, follow these naming and architectural conventions:

    Naming

    • PascalCase: Used for types and classes (e.g., Auth0Client, CacheManager).
    • camelCase: Used for members and functions.
    • Public Options: Defined as TypeScript interfaces located in global.ts.

    Dominant Patterns

    • Client Creation: Uses an async factory function createAuth0Client() which wraps the Auth0Client class.
    • Storage: Uses pluggable ICache storage backends, such as InMemoryCache and LocalStorageCache.
    • Error Handling: Uses typed errors that extend GenericError (defined in src/errors.ts).
    • OAuth Primitives: Wraps @auth0/auth0-auth-js for OAuth and MFA operations.
  12. Understand Online Refresh Tokens (ORTs)

    main

    Online Refresh Tokens (ORTs) are a refresh token type bound to the lifetime of the user's Auth0 session. They are useful for SPAs that want a renewal path that tracks the SSO session rather than living independently.

    Key Characteristics:

    • Session-bound: Valid only while the underlying Auth0 session is active. If the session ends (via logout, idle expiry, or admin revocation), the ORT stops working.
    • 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.
    • Requires DPoP: Because ORTs are non-rotating, sender-constraining the token via DPoP is mandatory to mitigate replay attacks. You must explicitly set useDpop: true.
    WARNING

    Online Refresh Tokens do not currently support resource servers with Ephemeral Sessions enabled. If a resource server has both allow_online_access and "Allow for Ephemeral Sessions" enabled, the ORT will be rejected with invalid_grant ("Unknown or invalid refresh token") on the next refresh. Disable "Allow for Ephemeral Sessions" on your resource server when using refreshTokenMode: RefreshTokenMode.Online.