UNPKG

@auth0/nextjs-auth0

Version:
264 lines (263 loc) 12.1 kB
import { NextResponse, type NextRequest } from "next/server.js"; import * as jose from "jose"; import { AccessTokenForConnectionError, ConnectAccountError, ConnectAccountErrorCodes, CustomTokenExchangeError, SdkError } from "../errors/index.js"; import { CompleteConnectAccountResponse, ConnectAccountOptions } from "../types/connected-accounts.js"; import { DpopKeyPair, DpopOptions } from "../types/dpop.js"; import { AccessTokenForConnectionOptions, AuthorizationParameters, BackchannelAuthenticationOptions, BackchannelAuthenticationResponse, ConnectionTokenSet, CustomTokenExchangeOptions, CustomTokenExchangeResponse, GetAccessTokenOptions, LogoutStrategy, RESPONSE_TYPES, SessionData, StartInteractiveLoginOptions, TokenSet } from "../types/index.js"; import { AccessTokenFactory, Fetcher, FetcherMinimalConfig } from "./fetcher.js"; import { AbstractSessionStore } from "./session/abstract-session-store.js"; import { TransactionStore } from "./transaction-store.js"; export type BeforeSessionSavedHook = (session: SessionData, idToken: string | null) => Promise<SessionData>; export type OnCallbackContext = { /** * The type of response expected from the authorization server. * One of {@link RESPONSE_TYPES} */ responseType?: RESPONSE_TYPES; /** * The URL or path the user should be redirected to after completing the transaction. */ returnTo?: string; /** * The connected account information when the responseType is {@link RESPONSE_TYPES.CONNECT_CODE} */ connectedAccount?: CompleteConnectAccountResponse; }; export type OnCallbackHook = (error: SdkError | null, ctx: OnCallbackContext, session: SessionData | null) => Promise<NextResponse>; export interface Routes { login: string; logout: string; callback: string; profile: string; accessToken: string; backChannelLogout: string; connectAccount: string; } export type RoutesOptions = Partial<Pick<Routes, "login" | "callback" | "logout" | "backChannelLogout" | "connectAccount">>; export interface AuthClientOptions { transactionStore: TransactionStore; sessionStore: AbstractSessionStore; domain: string; clientId: string; clientSecret?: string; clientAssertionSigningKey?: string | jose.CryptoKey; clientAssertionSigningAlg?: string; authorizationParameters?: AuthorizationParameters; pushedAuthorizationRequests?: boolean; secret: string; appBaseUrl: string; signInReturnToPath?: string; logoutStrategy?: LogoutStrategy; includeIdTokenHintInOIDCLogoutUrl?: boolean; beforeSessionSaved?: BeforeSessionSavedHook; onCallback?: OnCallbackHook; routes: Routes; fetch?: typeof fetch; jwksCache?: jose.JWKSCacheInput; allowInsecureRequests?: boolean; httpTimeout?: number; enableTelemetry?: boolean; enableAccessTokenEndpoint?: boolean; noContentProfileResponseWhenUnauthenticated?: boolean; enableConnectAccountEndpoint?: boolean; useDPoP?: boolean; dpopKeyPair?: DpopKeyPair; dpopOptions?: DpopOptions; } export declare class AuthClient { #private; private transactionStore; private sessionStore; private clientMetadata; private clientSecret?; private clientAssertionSigningKey?; private clientAssertionSigningAlg; private domain; private authorizationParameters; private pushedAuthorizationRequests; private appBaseUrl; private signInReturnToPath; private logoutStrategy; private includeIdTokenHintInOIDCLogoutUrl; private beforeSessionSaved?; private onCallback; private routes; private fetch; private jwksCache; private allowInsecureRequests; private httpTimeout; private httpOptions; private authorizationServerMetadata?; private readonly enableAccessTokenEndpoint; private readonly noContentProfileResponseWhenUnauthenticated; private readonly enableConnectAccountEndpoint; private dpopOptions?; private dpopKeyPair?; private readonly useDPoP; private proxyFetchers; constructor(options: AuthClientOptions); handler(req: NextRequest): Promise<NextResponse>; startInteractiveLogin(options?: StartInteractiveLoginOptions): Promise<NextResponse>; handleLogin(req: NextRequest): Promise<NextResponse>; handleLogout(req: NextRequest): Promise<NextResponse>; handleCallback(req: NextRequest): Promise<NextResponse>; handleProfile(req: NextRequest): Promise<NextResponse>; handleAccessToken(req: NextRequest): Promise<NextResponse>; handleBackChannelLogout(req: NextRequest): Promise<NextResponse>; handleConnectAccount(req: NextRequest): Promise<NextResponse>; handleMyAccount(req: NextRequest): Promise<NextResponse>; handleMyOrg(req: NextRequest): Promise<NextResponse>; /** * Retrieves OAuth token sets, handling token refresh when necessary or if forced. * * @returns A tuple containing either: * - `[SdkError, null]` if an error occurred (missing refresh token, discovery failure, or refresh failure) * - `[null, {tokenSet, idTokenClaims}]` if a new token was retrieved, containing the new token set ID token claims * - `[null, {tokenSet, }]` if token refresh was not done and existing token was returned */ getTokenSet(sessionData: SessionData, options?: GetAccessTokenOptions): Promise<[null, GetTokenSetResponse] | [SdkError, null]>; backchannelAuthentication(options: BackchannelAuthenticationOptions): Promise<[null, BackchannelAuthenticationResponse] | [SdkError, null]>; private discoverAuthorizationServerMetadata; private defaultOnCallback; /** * Handle callback errors with transaction cleanup */ private handleCallbackError; private verifyLogoutToken; private authorizationUrl; private getClientAuth; private get issuer(); /** * Exchanges a refresh token for an access token for a connection. * * This method performs a token exchange using the provided refresh token and connection details. * It first checks if the refresh token is present in the `tokenSet`. If not, it returns an error. * Then, it constructs the necessary parameters for the token exchange request and performs * the request to the authorization server's token endpoint. * * @returns {Promise<[AccessTokenForConnectionError, null] | [null, ConnectionTokenSet]>} A promise that resolves to a tuple. * The first element is either an `AccessTokenForConnectionError` if an error occurred, or `null` if the request was successful. * The second element is either `null` if an error occurred, or a `ConnectionTokenSet` object * containing the access token, expiration time, and scope if the request was successful. * * @throws {AccessTokenForConnectionError} If the refresh token is missing or if there is an error during the token exchange process. */ getConnectionTokenSet(tokenSet: TokenSet, connectionTokenSet: ConnectionTokenSet | undefined, options: AccessTokenForConnectionOptions): Promise<[ AccessTokenForConnectionError, null ] | [null, ConnectionTokenSet]>; /** * Validates that subject_token_type is a valid URI. * * Validation rules: * - Length: 10-100 characters * - Format: Valid URL or URN * * Note: Reserved namespace validation is handled by Auth0 when creating CTE profiles. * * @param type - The subject_token_type to validate * @returns CustomTokenExchangeError if invalid, null if valid */ private validateSubjectTokenType; /** * Exchanges an external token for Auth0 tokens via Custom Token Exchange (RFC 8693). * * This method allows you to exchange a token from an external identity provider * for Auth0 tokens without a browser redirect. The external token is validated * by your Auth0 Action with the Custom Token Exchange trigger. * * **Note**: CTE tokens are not cached per RWA SDK spec. The caller is responsible * for token storage if needed. * * @param options - The custom token exchange options * @returns A tuple of [error, null] or [null, response] * * @example * ```typescript * const [error, response] = await authClient.customTokenExchange({ * subjectToken: legacyIdToken, * subjectTokenType: 'urn:acme:legacy-token', * audience: 'https://api.example.com', * scope: 'read:data' * }); * ``` * * @see {@link https://auth0.com/docs/authenticate/custom-token-exchange Auth0 CTE Documentation} * @see {@link https://datatracker.ietf.org/doc/html/rfc8693 RFC 8693 Token Exchange} */ customTokenExchange(options: CustomTokenExchangeOptions): Promise<[ CustomTokenExchangeError, null ] | [null, CustomTokenExchangeResponse]>; /** * Filters and processes ID token claims for a session. * * If a `beforeSessionSaved` callback is configured, it will be invoked to allow * custom processing of the session and ID token. Otherwise, default filtering * will be applied to remove standard ID token claims from the user object. */ finalizeSession(session: SessionData, idToken?: string): Promise<SessionData>; /** * Initiates the connect account flow for linking a third-party account to the user's profile. * The user will be redirected to authorize the connection. */ connectAccount(options: ConnectAccountOptions & { tokenSet: TokenSet; }): Promise<[ConnectAccountError, null] | [null, NextResponse]>; private createConnectAccountTicket; private completeConnectAccount; private getOpenIdClientConfig; /** * Creates a new Fetcher instance with DPoP support and authentication capabilities. * * This method creates fetcher-scoped DPoP handles via `oauth.DPoP(this.clientMetadata, this.dpopKeyPair!)`. * Each fetcher instance maintains its own DPoP nonce state for isolation and security. * It is recommended to create fetchers at module level and reuse them across requests * * @example Recommended fetcher reuse pattern * ```typescript * const managementApi = await auth0.fetcherFactory({ * baseUrl: `https://${process.env.AUTH0_DOMAIN}/api/v2/`, * session: await getSession(req, res) * }); * * // Use the same fetcher for multiple requests * const users = await managementApi.get('users'); * const roles = await managementApi.get('roles'); * ``` * * **DPoP Nonce Management:** * - Each fetcher learns and caches nonces from the authorization server * - Failed nonce validation triggers automatic retry with updated nonce * - Nonce state is isolated between fetcher instances for security * * @param options Configuration options for the fetcher * @returns Promise resolving to a configured Fetcher instance * @throws {DPoPError} When DPoP is enabled but no keypair is configured */ fetcherFactory<TOutput extends Response>(options: FetcherFactoryOptions<TOutput>): Promise<Fetcher<TOutput>>; } type GetTokenSetResponse = { tokenSet: TokenSet; idTokenClaims?: { [key: string]: any; }; }; /** * Options for creating a Fetcher instance via the factory method. * * Includes all FetcherMinimalConfig options plus internal session data. * The `nonceStorageId` from FetcherMinimalConfig is included but currently ignored. */ export type FetcherFactoryOptions<TOutput extends Response> = { useDPoP?: boolean; getAccessToken: AccessTokenFactory; } & FetcherMinimalConfig<TOutput>; /** * Builds a ConnectAccountError response based on the provided Response object and error code. * @param res The Response object containing the error details. * @param errorCode The ConnectAccountErrorCodes enum value representing the type of error. * @returns */ export declare function buildConnectAccountErrorResponse(res: Response, errorCode: ConnectAccountErrorCodes): Promise<[ConnectAccountError, null]>; export {};