Auth0 Next.js SDK

repository·main·Indexed 25 days ago

https://github.com/auth0/nextjs-auth0

A library for implementing user authentication in Next.js applications, providing server-side authentication methods and middleware for intercepting requests. Version 4.26.0 supports advanced security features including Custom Token Exchange (CTE) for RFC 8693 compliance, Session Transfer Tokens (STT), and server-side Demonstrating Proof-of-Possession (DPoP) to bind access tokens to cryptographic key pairs.

Tokens
81.7K
Snippets
188
Records
270
Agent score
79%

What's inside @auth0/nextjs-auth0

  1. Key changes in v4 migration

    main

    When migrating from v3 to v4, note the following behavioral and API changes:

    • Edge Compatibility: v4 is edge-compatible by default; the @auth0/nextjs-auth0/edge export has been removed.
    • Cookie Defaults: All cookies set by the SDK now default to SameSite=Lax.
    • Session Management: The touchSession method has been removed. Rolling sessions are now enabled by default in the middleware.
    • Access Tokens: getAccessToken can now be called directly in React Server Components.
    • Logout Behavior: v4 uses OpenID Connect's RP-Initiated Logout by default if enabled on your Auth0 tenant; otherwise, it falls back to the /v2/logout endpoint.
    • Customization: Customizing auth handlers via handleAuth is replaced by Middleware Interception and the onCallback hook.
  2. How the Proxy Handler for My Account and My Organization APIs works

    main

    The SDK provides a built-in proxy handler that implements a Backend-for-Frontend (BFF) pattern for Auth0's My Account and My Organization Management APIs. This allows client-side JavaScript to make requests to these APIs through your Next.js server, ensuring that DPoP private keys, tokens, and session management remain secure on the server side.

    Key features:

    • Security: DPoP private keys and tokens are never exposed to the browser.
    • Automation: Automatically retrieves/refreshes tokens for the requested audience and scope, generates DPoP proofs, and updates the session.
    • Transparency: The proxy handler is automatically enabled when using the SDK's middleware.

    Request Flow:

    1. Client requests /me/* (My Account) or /my-org/* (My Organization).
    2. Middleware intercepts the request and validates the session.
    3. Middleware retrieves/refreshes the access token for the specific audience.
    4. Middleware generates a DPoP proof (if enabled).
    5. The request is forwarded to Auth0 with proper authentication headers.
    6. The response is returned to the client, and the session is updated if tokens were refreshed.
  3. How Passwordless Auth Routes Work

    main

    When using the headless API, the SDK utilizes specific internal routes to manage the passwordless lifecycle. These routes are typically handled by a middleware (like proxy.ts) that passes requests through auth0.middleware().

    MethodPathDescription
    POST/auth/passwordless/startCalls Auth0 to send an OTP or magic link to the user
    POST/auth/passwordless/verifyVerifies the OTP, exchanges it for tokens, and sets the session cookie
    GET/auth/callbackHandles the redirect after a magic link is clicked (standard OAuth callback)
  4. Perform Custom Token Exchange with Actor Token (Delegation)

    main

    For delegation scenarios (RFC 8693 §4.1) where an agent acts on behalf of a user, provide an actorToken and actorTokenType.

    Key Behaviors:

    • act claim: The act claim is automatically decoded from the ID token and returned in the result. It is also accessible via session.user.act if the flow is part of a session-creating process.
    • Refresh Tokens: Auth0 suppresses refresh tokens in delegation flows. result.refreshToken will be undefined by design.
    const result = await auth0.customTokenExchange({
      subjectToken: userToken,
      subjectTokenType: "urn:acme:user-token",
      actorToken: agentToken,
      actorTokenType: "https://idp.example.com/token-type/agent",
      audience: "https://downstream-api.example.com"
    });
    
    // act claim decoded from the ID token
    console.log(result.act);
    // { sub: "agent|abc123" }
  5. Handle MFA during Passwordless Login

    main

    When a user has Multi-Factor Authentication (MFA) enabled, the loginWithOtp method will throw a mfa_required error. You can handle this by using the mfa object from @auth0/nextjs-auth0/client to manage the challenge flow:

    1. Detect MFA: Catch the mfa_required error.
    2. Retrieve Authenticators: Call mfa.getAuthenticators() to see if the user has an active authenticator (like TOTP) or needs to enroll.
    3. Enrollment: If no active authenticator exists, guide the user through TOTP enrollment using the provided QR code data.
    4. Verification: Use mfa.verify() to complete the authentication process once the user provides their TOTP or OOB code.
  6. How DPoP configuration inheritance works

    main

    DPoP configuration follows a specific precedence order when using createFetcher:

    1. Explicit fetcher option: options.useDPoP (if specified in createFetcher)
    2. Global Auth0Client setting: auth0.useDPoP (if the fetcher option is not specified)
    3. Default: false (if neither is configured)

    This allows you to enable DPoP globally for your application while still being able to override it for specific legacy or non-secure APIs.

  7. mTLS support and limitations

    main

    Supported Flows

    • Authorization code exchange: Full support; issues cnf.x5t#S256 claims.
    • Refresh token grant: Full support; issues cnf.x5t#S256 claims.
    • Client credentials (M2M): Full support; issues cnf.x5t#S256 claims.
    • Interactive flows: MFA step-up (mfaVerify), Passkey login (passkeyGetToken), and Custom token exchange are supported via mTLS aliases.

    Known Gaps

    • Passwordless Start: The POST /passwordless/start endpoint does not currently accept mTLS client authentication. An mTLS-only client (without a client_secret) will receive an invalid_client error when calling this specific endpoint. However, the subsequent passwordlessVerify step works correctly.
    • Client Credentials Method: The SDK does not currently expose a dedicated clientCredentials() method, though Auth0 supports this flow with mTLS.
  8. Configure Multi-Resource Refresh Tokens (MRRT)

    main

    Multi-Resource Refresh Tokens allow a single refresh token to obtain access tokens for multiple audiences.

    Configuration:

    1. In the Auth0Client constructor, you can provide an object for scope where keys are audience identifiers and values are the corresponding scope strings.
    2. When calling getAccessToken({ audience: '...' }), the SDK automatically selects the correct scopes for that audience.

    Best Practices:

    • Broad Default Scopes: Define common scopes in the constructor to minimize dynamic requests.
    • Minimize Dynamic Scopes: Avoid passing scope in getAccessToken() calls unless necessary, as each unique audience + scope combination increases session size.
    // lib/auth0.ts
    import { Auth0Client } from "@auth0/nextjs-auth0/server";
    
    export const auth0 = new Auth0Client({
      authorizationParameters: {
        audience: "https://api.example.com",
        scope: {
          "https://api.example.com": "openid profile email offline_access read:products",
          "https://analytics.example.com": "openid profile email read:analytics"
        }
      }
    });
    
    // Usage
    const dataToken = await auth0.getAccessToken({
      audience: "https://analytics.example.com"
    });
  9. Handle Step-up Authentication with `MfaRequiredError`

    main

    Step-up authentication allows you to require a stronger mechanism (like MFA) when a user attempts to access sensitive resources.

    When an API requires MFA, Auth0 returns a 403 Forbidden with an mfa_required error code. The SDK catches this and throws an MfaRequiredError, which contains the mfa_token needed to complete the challenge.

    Implementation Workflow

    1. Server Side: Catch MfaRequiredError in your API route or Server Action and forward the error details (including the mfa_token) to the client using error.toJSON().
    2. Client Side: Upon receiving the 403 error, either redirect the user to an MFA challenge page (passing the mfa_token in the URL) or use the mfa.challengeWithPopup() method for a non-redirecting experience.
    // Server Side (API Route)
    import { NextResponse } from "next/server";
    import { auth0 } from "@/lib/auth0";
    import { MfaRequiredError } from "@auth0/nextjs-auth0/server";
    
    export async function GET() {
      try {
        const { token } = await auth0.getAccessToken({
          audience: "https://my-high-security-api",
          refresh: true // Ensure we get a fresh token check
        });
        return NextResponse.json({ token });
      } catch (error) {
        if (error instanceof MfaRequiredError) {
          // Forward the error details to the client
          return NextResponse.json(error.toJSON(), { status: 403 });
        }
        throw error;
      }
    }
  10. Compare Universal Login vs. Custom Passkey Login

    main

    When implementing passkeys, you can choose between Auth0's hosted experience or building your own UI.

    FeatureUniversal LoginCustom Login (headless)
    UIAuth0-hostedYour own components
    Code requiredOne linkForm + WebAuthn ceremony
    SecurityCredentials stay with Auth0Private key never leaves device
    CustomisationAuth0 Dashboard brandingComplete
    Recommended✅ DefaultAdvanced / full UI control

    Using Universal Login

    To use the simplest and most secure approach, redirect users to the /auth/login route:

    <a href="/auth/login">Continue with Universal Login</a>
  11. Customize ID token claims in the user object

    main

    In v4, to prevent large cookies, only a specific set of default claims are persisted in the session user object:

    • sub, name, nickname, given_name, family_name, picture, email, email_verified, org_id.

    To include additional custom claims from the ID token in the session, use the beforeSessionSaved hook.

  12. Handle session expiry from upstream Identity Providers

    main

    For enterprise connections, an upstream identity provider may cap the session lifetime. If configured to honor this, Auth0 includes a session_expiry claim in the ID token (provided via a Post-Login Action as Unix seconds).

    The SDK enforces this ceiling. When the session_expiry time is reached:

    • getSession() returns null.
    • useUser() reflects the logged-out state.
    • getAccessToken() throws an AccessTokenError with the code session_expired.

    Best Practice: Apps should not assume a session always exists after login. Always implement a null check for the session and redirect users back to the login route if the session has expired.