UNPKG

@auth0/nextjs-auth0

Version:
464 lines (463 loc) 21 kB
import type { IncomingMessage, ServerResponse } from "http"; import { NextRequest, NextResponse } from "next/server.js"; import { NextApiHandler, NextApiRequest, NextApiResponse } from "next/types.js"; import { DpopKeyPair, DpopOptions } from "../types/dpop.js"; import { AccessTokenForConnectionOptions, AuthorizationParameters, BackchannelAuthenticationOptions, ConnectAccountOptions, GetAccessTokenOptions, LogoutStrategy, SessionData, SessionDataStore, StartInteractiveLoginOptions } from "../types/index.js"; import { BeforeSessionSavedHook, OnCallbackHook, RoutesOptions } from "./auth-client.js"; import { AccessTokenFactory, CustomFetchImpl, Fetcher } from "./fetcher.js"; import * as withApiAuthRequired from "./helpers/with-api-auth-required.js"; import { AppRouterPageRoute, WithPageAuthRequiredAppRouterOptions, WithPageAuthRequiredPageRouterOptions } from "./helpers/with-page-auth-required.js"; import { SessionConfiguration } from "./session/abstract-session-store.js"; import { TransactionCookieOptions } from "./transaction-store.js"; export interface Auth0ClientOptions { /** * The Auth0 domain for the tenant (e.g.: `example.us.auth0.com`). * * If it's not specified, it will be loaded from the `AUTH0_DOMAIN` environment variable. */ domain?: string; /** * The Auth0 client ID. * * If it's not specified, it will be loaded from the `AUTH0_CLIENT_ID` environment variable. */ clientId?: string; /** * The Auth0 client secret. * * If it's not specified, it will be loaded from the `AUTH0_CLIENT_SECRET` environment variable. */ clientSecret?: string; /** * Additional parameters to send to the `/authorize` endpoint. */ authorizationParameters?: AuthorizationParameters; /** * If enabled, the SDK will use the Pushed Authorization Requests (PAR) protocol when communicating with the authorization server. */ pushedAuthorizationRequests?: boolean; /** * Private key for use with `private_key_jwt` clients. * This should be a string that is the contents of a PEM file or a CryptoKey. */ clientAssertionSigningKey?: string | CryptoKey; /** * The algorithm used to sign the client assertion JWT. * Uses one of `token_endpoint_auth_signing_alg_values_supported` if not specified. * If the Authorization Server discovery document does not list `token_endpoint_auth_signing_alg_values_supported` * this property will be required. */ clientAssertionSigningAlg?: string; /** * The URL of your application (e.g.: `http://localhost:3000`). * * If it's not specified, it will be loaded from the `APP_BASE_URL` environment variable. */ appBaseUrl?: string; /** * A 32-byte, hex-encoded secret used for encrypting cookies. * * If it's not specified, it will be loaded from the `AUTH0_SECRET` environment variable. */ secret?: string; /** * The path to redirect the user to after successfully authenticating. Defaults to `/`. */ signInReturnToPath?: string; /** * Configure the session timeouts and whether to use rolling sessions or not. * * See [Session configuration](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#session-configuration) for additional details. */ session?: SessionConfiguration; /** * Configure the transaction cookie used to store the state of the authentication transaction. */ transactionCookie?: TransactionCookieOptions; /** * Configure the logout strategy to use. * * - `'auto'` (default): Attempts OIDC RP-Initiated Logout first, falls back to `/v2/logout` if not supported * - `'oidc'`: Always uses OIDC RP-Initiated Logout (requires RP-Initiated Logout to be enabled) * - `'v2'`: Always uses the Auth0 `/v2/logout` endpoint (supports wildcards in allowed logout URLs) */ logoutStrategy?: LogoutStrategy; /** * Configure whether to include id_token_hint in OIDC logout URLs. * * **Recommended (default)**: Set to `true` to include `id_token_hint` parameter. * Auth0 recommends using `id_token_hint` for secure logout as per the * OIDC specification. * * **Alternative approach**: Set to `false` if your application cannot securely * store ID tokens. When disabled, only `logout_hint` (session ID), `client_id`, * and `post_logout_redirect_uri` are sent. * * * @see https://auth0.com/docs/authenticate/login/logout/log-users-out-of-auth0#oidc-logout-endpoint-parameters * @default true (recommended and backwards compatible) */ includeIdTokenHintInOIDCLogoutUrl?: boolean; /** * A method to manipulate the session before persisting it. * * See [beforeSessionSaved](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#beforesessionsaved) for additional details */ beforeSessionSaved?: BeforeSessionSavedHook; /** * A method to handle errors or manage redirects after attempting to authenticate. * * See [onCallback](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#oncallback) for additional details */ onCallback?: OnCallbackHook; /** * A custom session store implementation used to persist sessions to a data store. * * See [Database sessions](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#database-sessions) for additional details. */ sessionStore?: SessionDataStore; /** * Configure the paths for the authentication routes. * * See [Custom routes](https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#custom-routes) for additional details. */ routes?: RoutesOptions; /** * Allow insecure requests to be made to the authorization server. This can be useful when testing * with a mock OIDC provider that does not support TLS, locally. * This option can only be used when NODE_ENV is not set to `production`. */ allowInsecureRequests?: boolean; /** * Integer value for the HTTP timeout in milliseconds for authentication requests. * Defaults to `5000` ms. */ httpTimeout?: number; /** * Boolean value to opt-out of sending the library name and version to your authorization server * via the `Auth0-Client` header. Defaults to `true`. */ enableTelemetry?: boolean; /** * Boolean value to enable the `/auth/access-token` endpoint for use in the client app. * * Defaults to `true`. * * NOTE: Set this to `false` if your client does not need to directly interact with resource servers (Token Mediating Backend). This will be false for most apps. * * A security best practice is to disable this to avoid exposing access tokens to the client app. * * See: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#name-token-mediating-backend */ enableAccessTokenEndpoint?: boolean; /** * If true, the profile endpoint will return a 204 No Content response when the user is not authenticated * instead of returning a 401 Unauthorized response. * * Defaults to `false`. */ noContentProfileResponseWhenUnauthenticated?: boolean; enableParallelTransactions?: boolean; /** * If true, the `/auth/connect` endpoint will be mounted to enable users to connect additional accounts. */ enableConnectAccountEndpoint?: boolean; /** * Enable DPoP (Demonstrating Proof-of-Possession) for enhanced OAuth 2.0 security. * * When enabled, the SDK will: * - Generate DPoP proofs for token requests and protected resource requests * - Bind access tokens cryptographically to the client's key pair * - Prevent token theft and replay attacks * - Handle DPoP nonce errors with automatic retry logic * * DPoP requires an ES256 key pair that can be provided via `dpopKeyPair` option * or loaded from environment variables `AUTH0_DPOP_PUBLIC_KEY` and `AUTH0_DPOP_PRIVATE_KEY`. * * @default false * * @example Enable DPoP with generated keys * ```typescript * import { generateKeyPair } from "oauth4webapi"; * * const dpopKeyPair = await generateKeyPair("ES256"); * * const auth0 = new Auth0Client({ * useDPoP: true, * dpopKeyPair * }); * ``` * * @example Enable DPoP with environment variables * ```typescript * // .env.local * // AUTH0_DPOP_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----..." * // AUTH0_DPOP_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..." * * const auth0 = new Auth0Client({ * useDPoP: true * // Keys loaded automatically from environment * }); * ``` * * @see {@link https://datatracker.ietf.org/doc/html/rfc9449 | RFC 9449: OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)} */ useDPoP?: boolean; /** * ES256 key pair for DPoP proof generation. * * If not provided when `useDPoP` is true, the SDK will attempt to load keys from * environment variables `AUTH0_DPOP_PUBLIC_KEY` and `AUTH0_DPOP_PRIVATE_KEY`. * Keys must be in PEM format and use the P-256 elliptic curve. * * @example Provide key pair directly * ```typescript * import { generateKeyPair } from "oauth4webapi"; * * const keyPair = await generateKeyPair("ES256"); * * const auth0 = new Auth0Client({ * useDPoP: true, * dpopKeyPair: keyPair * }); * ``` * * @example Load from files * ```typescript * import { importSPKI, importPKCS8 } from "jose"; * import { readFileSync } from "fs"; * * const publicKeyPem = readFileSync("dpop-public.pem", "utf8"); * const privateKeyPem = readFileSync("dpop-private.pem", "utf8"); * * const auth0 = new Auth0Client({ * useDPoP: true, * dpopKeyPair: { * publicKey: await importSPKI(publicKeyPem, "ES256"), * privateKey: await importPKCS8(privateKeyPem, "ES256") * } * }); * ``` * * @see {@link DpopKeyPair} for the key pair interface * @see {@link generateDpopKeyPair} for generating new key pairs */ dpopKeyPair?: DpopKeyPair; /** * Configuration options for DPoP timing validation and retry behavior. * * These options control how the SDK validates DPoP proof timing and handles * nonce errors. Proper configuration is important for both security and reliability. * * @example Basic configuration * ```typescript * const auth0 = new Auth0Client({ * useDPoP: true, * dpopOptions: { * clockTolerance: 60, // Allow 60 seconds clock difference * clockSkew: 0, // No clock adjustment needed * retry: { * delay: 200, // 200ms delay before retry * jitter: true // Add randomness to prevent thundering herd * } * } * }); * ``` * * @example Environment variable configuration * ```bash * # .env.local * AUTH0_DPOP_CLOCK_SKEW=0 * AUTH0_DPOP_CLOCK_TOLERANCE=30 * AUTH0_RETRY_DELAY=100 * AUTH0_RETRY_JITTER=true * ``` * * @see {@link DpopOptions} for detailed option descriptions */ dpopOptions?: DpopOptions; } export type PagesRouterRequest = IncomingMessage | NextApiRequest; export type PagesRouterResponse = ServerResponse<IncomingMessage> | NextApiResponse; export declare class Auth0Client { #private; private transactionStore; private sessionStore; private authClient; private routes; private domain; constructor(options?: Auth0ClientOptions); /** * middleware mounts the SDK routes to run as a middleware function. */ middleware(req: NextRequest): Promise<NextResponse>; /** * getSession returns the session data for the current request. * * This method can be used in Server Components, Server Actions, and Route Handlers in the **App Router**. */ getSession(): Promise<SessionData | null>; /** * getSession returns the session data for the current request. * * This method can be used in middleware and `getServerSideProps`, API routes in the **Pages Router**. */ getSession(req: PagesRouterRequest | NextRequest): Promise<SessionData | null>; /** * getAccessToken returns the access token. * * This method can be used in Server Components, Server Actions, and Route Handlers in the **App Router**. * * NOTE: Server Components cannot set cookies. Calling `getAccessToken()` in a Server Component will cause the access token to be refreshed, if it is expired, and the updated token set will not to be persisted. * It is recommended to call `getAccessToken(req, res)` in the middleware if you need to retrieve the access token in a Server Component to ensure the updated token set is persisted. */ /** * @param options Optional configuration for getting the access token. * @param options.refresh Force a refresh of the access token. */ getAccessToken(options?: GetAccessTokenOptions): Promise<{ token: string; expiresAt: number; scope?: string; token_type?: string; audience?: string; }>; /** * getAccessToken returns the access token. * * This method can be used in middleware and `getServerSideProps`, API routes in the **Pages Router**. * * @param req The request object. * @param res The response object. * @param options Optional configuration for getting the access token. * @param options.refresh Force a refresh of the access token. */ getAccessToken(req: PagesRouterRequest | NextRequest, res: PagesRouterResponse | NextResponse, options?: GetAccessTokenOptions): Promise<{ token: string; expiresAt: number; scope?: string; token_type?: string; audience?: string; }>; /** * Core implementation of getAccessToken that performs the actual token retrieval. * This is separated to enable request coalescing via the cache. */ private executeGetAccessToken; /** * Retrieves an access token for a connection. * * This method can be used in Server Components, Server Actions, and Route Handlers in the **App Router**. * * NOTE: Server Components cannot set cookies. Calling `getAccessTokenForConnection()` in a Server Component will cause the access token to be refreshed, if it is expired, and the updated token set will not to be persisted. * It is recommended to call `getAccessTokenForConnection(req, res)` in the middleware if you need to retrieve the access token in a Server Component to ensure the updated token set is persisted. */ getAccessTokenForConnection(options: AccessTokenForConnectionOptions): Promise<{ token: string; expiresAt: number; }>; /** * Retrieves an access token for a connection. * * This method can be used in middleware and `getServerSideProps`, API routes in the **Pages Router**. */ getAccessTokenForConnection(options: AccessTokenForConnectionOptions, req: PagesRouterRequest | NextRequest | undefined, res: PagesRouterResponse | NextResponse | undefined): Promise<{ token: string; expiresAt: number; }>; /** * updateSession updates the session of the currently authenticated user. If the user does not have a session, an error is thrown. * * This method can be used in middleware and `getServerSideProps`, API routes, and middleware in the **Pages Router**. */ updateSession(req: PagesRouterRequest | NextRequest, res: PagesRouterResponse | NextResponse, session: SessionData): Promise<void>; /** * updateSession updates the session of the currently authenticated user. If the user does not have a session, an error is thrown. * * This method can be used in Server Actions and Route Handlers in the **App Router**. */ updateSession(session: SessionData): Promise<void>; private createRequestCookies; startInteractiveLogin(options?: StartInteractiveLoginOptions): Promise<NextResponse>; /** * Authenticates using Client-Initiated Backchannel Authentication and returns the token set and optionally the ID token claims and authorization details. * * This method will initialize the backchannel authentication process with Auth0, and poll the token endpoint until the authentication is complete. * * Using Client-Initiated Backchannel Authentication requires the feature to be enabled in the Auth0 dashboard. * @see https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-initiated-backchannel-authentication-flow */ getTokenByBackchannelAuth(options: BackchannelAuthenticationOptions): Promise<import("../types/index.js").BackchannelAuthenticationResponse>; /** * Initiates the Connect Account flow to connect a third-party account to the user's profile. * If the user does not have an active session, a `ConnectAccountError` is thrown. * * This method first attempts to obtain an access token with the `create:me:connected_accounts` scope * for the My Account API to create a connected account for the user. * * The user will then be redirected to authorize the connection with the third-party provider. */ connectAccount(options: ConnectAccountOptions): Promise<NextResponse>; withPageAuthRequired(fnOrOpts?: WithPageAuthRequiredPageRouterOptions | AppRouterPageRoute, opts?: WithPageAuthRequiredAppRouterOptions): AppRouterPageRoute | import("./helpers/with-page-auth-required.js").PageRoute<{ [key: string]: any; }, import("querystring").ParsedUrlQuery>; withApiAuthRequired(apiRoute: withApiAuthRequired.AppRouteHandlerFn | NextApiHandler): (req: NextRequest | NextApiRequest, resOrParams: withApiAuthRequired.AppRouteHandlerFnContext | NextApiResponse) => unknown; private saveToSession; /** * Validates and extracts required configuration options. * @param options The client options * @returns The validated required options * @throws ConfigurationError if any required option is missing */ private validateAndExtractRequiredOptions; /** * Creates a configured Fetcher instance for making authenticated API requests. * * This method creates a specialized HTTP client that handles: * - Automatic access token retrieval and injection * - DPoP (Demonstrating Proof-of-Possession) proof generation when enabled * - Token refresh and session management * - Error handling and retry logic for DPoP nonce errors * - Base URL resolution for relative requests * * The fetcher provides a high-level interface for making requests to protected resources * without manually handling authentication details. * * @template TOutput - Response type that extends the standard Response interface * @param req - Request object for session context (required for Pages Router, optional for App Router) * @param options - Configuration options for the fetcher * @param options.useDPoP - Enable DPoP for this fetcher instance (overrides global setting) * @param options.baseUrl - Base URL for resolving relative requests * @param options.getAccessToken - Custom access token factory function * @param options.fetch - Custom fetch implementation * @returns Promise that resolves to a configured Fetcher instance * @throws AccessTokenError when no active session exists * * @example * ```typescript * import { auth0 } from "@/lib/auth0"; * * const fetcher = await auth0.createFetcher(undefined, { * baseUrl: "https://api.example.com", * useDPoP: true * }); * * const response = await fetcher.fetchWithAuth("/users"); * const users = await response.json(); * ``` * * @see {@link Fetcher} for details on using the returned fetcher instance * @see {@link FetcherMinimalConfig} for available configuration options */ createFetcher<TOutput extends Response = Response>(req: PagesRouterRequest | NextRequest | undefined, options: { /** Enable DPoP for this fetcher instance (overrides global setting) */ useDPoP?: boolean; /** Custom access token factory function. If not provided, uses the default from hooks */ getAccessToken?: AccessTokenFactory; /** Base URL for relative requests. Must be provided if using relative URLs */ baseUrl?: string; /** Custom fetch implementation. Falls back to global fetch if not provided */ fetch?: CustomFetchImpl<TOutput>; }): Promise<Fetcher<TOutput>>; private get issuer(); }