import { NextResponse, type NextRequest } from "next/server.js"; import { RequestCookies, ResponseCookies } from "@edge-runtime/cookies"; import * as jose from "jose"; import * as oauth from "oauth4webapi"; import * as client from "openid-client"; import packageJson from "../../package.json" with { type: "json" }; import { AccessTokenError, AccessTokenErrorCode, AccessTokenForConnectionError, AccessTokenForConnectionErrorCode, AuthorizationCodeGrantError, AuthorizationCodeGrantRequestError, AuthorizationError, BackchannelAuthenticationError, BackchannelAuthenticationNotSupportedError, BackchannelLogoutError, ConnectAccountError, ConnectAccountErrorCodes, CustomTokenExchangeError, CustomTokenExchangeErrorCode, DiscoveryError, DPoPError, DPoPErrorCode, InvalidConfigurationError, InvalidStateError, MfaChallengeError, MfaEnrollmentError, MfaGetAuthenticatorsError, MfaNoAvailableFactorsError, MfaRequiredError, MfaVerifyError, MissingStateError, MtlsError, MtlsErrorCode, MyAccountApiError, OAuth2Error, PasskeyChallengeError, PasskeyEnrollmentChallengeError, PasskeyEnrollmentVerifyError, PasskeyGetTokenError, PasskeyRegisterError, PasswordlessDbChallengeError, PasswordlessDbGetTokenError, PasswordlessStartError, PasswordlessVerifyError, SdkError, TokenRevocationError, TokenRevocationErrorCode } from "../errors/index.js"; import { IssuerValidationError, SessionDomainMismatchError } from "../errors/mcd.js"; import { CompleteConnectAccountRequest, CompleteConnectAccountResponse, ConnectAccountOptions, ConnectAccountRequest, ConnectAccountResponse } from "../types/connected-accounts.js"; import { DpopKeyPair, DpopOptions } from "../types/dpop.js"; import { AccessTokenForConnectionOptions, AccessTokenSet, ActClaim, AuthenticatorApiResponse, AuthorizationParameters, BackchannelAuthenticationOptions, BackchannelAuthenticationResponse, ChallengeApiResponse, ConnectionTokenSet, CustomTokenExchangeOptions, CustomTokenExchangeResponse, EnrollmentApiResponse, EnrollOobOptions, EnrollOtpOptions, GetAccessTokenOptions, GRANT_TYPE_CUSTOM_TOKEN_EXCHANGE, GRANT_TYPE_PASSKEY, GRANT_TYPE_PASSWORDLESS_OTP, LogoutStrategy, LogoutToken, PasskeyChallengeOptions, PasskeyChallengeResponse, PasskeyEnrollmentChallengeOptions, PasskeyEnrollmentChallengeResponse, PasskeyEnrollmentVerifyOptions, PasskeyEnrollmentVerifyResponse, PasskeyGetTokenOptions, PasskeyRegisterOptions, PasskeyRegisterResponse, PasswordlessDbChallenge, PasswordlessDbChallengeEmailOptions, PasswordlessDbChallengePhoneOptions, PasswordlessDbGetTokenOptions, PasswordlessStartOptions, PasswordlessVerifyOptions, PasswordlessVerifyTokenResponse, ProxyOptions, RESPONSE_TYPES, SessionData, SessionTransferTokenOptions, SessionTransferTokenResult, StartInteractiveLoginOptions, SUBJECT_TOKEN_TYPES, TOKEN_TYPES, TokenSet, User, VerifyMfaOptions } from "../types/index.js"; import type { SessionCheckResult } from "../types/mcd.js"; import type { MfaTokenEndpointResponse } from "../types/mfa.js"; import { resolveAppBaseUrl } from "../utils/app-base-url.js"; import { mergeAuthorizationParamsIntoSearchParams, parseNonNegativeIntegerParam } from "../utils/authorization-params-helpers.js"; import { DEFAULT_MFA_CONTEXT_TTL_SECONDS, DEFAULT_SCOPES, DEFAULT_STT_SCOPES } from "../utils/constants.js"; import { withDPoPNonceRetry } from "../utils/dpopRetry.js"; import { createSizeLimitedFetch } from "../utils/fetchUtils.js"; import { createAuthCompletePostMessageResponse } from "../utils/html-helpers.js"; import { buildEnrollOptions } from "../utils/mfa-server-utils.js"; import { buildVerifyParams, getVerifyGrantType, transformVerifyBodyToOptions } from "../utils/mfa-transform-utils.js"; import { decryptMfaToken, encryptMfaToken, extractMfaErrorDetails, handleMfaError, isMfaRequiredError } from "../utils/mfa-utils.js"; import { extractMfaToken, parseJsonBody, validateArrayFieldAndThrow, validateStringFieldAndThrow, validateVerificationCredentialAndThrow } from "../utils/mfa-validation-utils.js"; import { normalizeDomain, normalizeIssuer } from "../utils/normalize.js"; import { extractOAuthErrorDetails } from "../utils/oauth-error-utils.js"; import { createRouteUrl, removeTrailingSlash } from "../utils/pathUtils.js"; import { buildForwardedRequestHeaders, buildForwardedResponseHeaders, transformTargetUrl } from "../utils/proxy.js"; import { ensureDefaultScope, getScopeForAudience } from "../utils/scope-helpers.js"; import { getSessionChangesAfterGetAccessToken } from "../utils/session-changes-helpers.js"; import { buildSessionFromCallback, isSessionCeilingInPast, isSessionCeilingReached, mergePopupTokenIntoSession } from "../utils/session-helpers.js"; import { buildSessionTransferAudience, buildSessionTransferRedirectUrl, mapSttServerError, parseSessionTransferTokenResponse, resolveActorFromSession } from "../utils/session-transfer-helpers.js"; import { compareScopes, findAccessTokenSet, isBeforeOrEqual, mergeScopes, normalizeExpiresAt, normalizeTokenType, tokenSetFromAccessTokenSet } from "../utils/token-set-helpers.js"; import { isUrl, toSafeRedirect } from "../utils/url-helpers.js"; import type { AuthClientProvider } from "./auth-client-provider.js"; import { addCacheControlHeadersForSession, type ReadonlyRequestCookies } from "./cookies.js"; import { DiscoveryCache } from "./discovery-cache.js"; import { AccessTokenFactory, Fetcher, FetcherConfig, FetcherHooks, FetcherMinimalConfig } from "./fetcher.js"; import { AbstractSessionStore } from "./session/abstract-session-store.js"; import { TransactionState, TransactionStore } from "./transaction-store.js"; import { filterDefaultIdTokenClaims } from "./user.js"; export type BeforeSessionSavedHook = ( session: SessionData, idToken: string | null ) => Promise; export type OnCallbackContext = { /** * The type of response expected from the authorization server. * One of {@link RESPONSE_TYPES} */ responseType?: RESPONSE_TYPES; /** * The resolved base URL for the current request, used to build safe redirects. */ appBaseUrl?: string; /** * 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; /** * The return strategy for this callback flow. * - 'redirect' (default): Standard OAuth redirect flow * - 'popup': Popup flow returning via window.postMessage * Hook authors can use this to detect popup flows and adapt behavior. */ challengeMode?: "redirect" | "popup"; }; export type OnCallbackHook = ( error: SdkError | null, ctx: OnCallbackContext, session: SessionData | null ) => Promise; // params passed to the /authorize endpoint that cannot be overwritten const INTERNAL_AUTHORIZE_PARAMS = [ "client_id", "redirect_uri", "response_type", "code_challenge", "code_challenge_method", "state", "nonce" ]; /** * A constant representing the grant type for federated connection access token exchange. * * This grant type is used in OAuth token exchange scenarios where a federated connection * access token is required. It is specific to Auth0's implementation and follows the * "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token" format. */ const GRANT_TYPE_FEDERATED_CONNECTION_ACCESS_TOKEN = "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token"; /** * A constant representing the token type for federated connection access tokens. * This is used to specify the type of token being requested from Auth0. * * @constant * @type {string} */ const REQUESTED_TOKEN_TYPE_FEDERATED_CONNECTION_ACCESS_TOKEN = "http://auth0.com/oauth/token-type/federated-connection-access-token"; export interface Routes { login: string; logout: string; callback: string; profile: string; accessToken: string; backChannelLogout: string; connectAccount: string; mfaAuthenticators: string; mfaChallenge: string; mfaVerify: string; mfaAssociate: string; passwordlessStart: string; passwordlessVerify: string; passwordlessDbOtpChallenge: string; passwordlessDbGetToken: string; passkeyRegister: string; passkeyChallenge: string; passkeyGetToken: string; passkeyEnrollmentChallenge: string; passkeyEnrollmentVerify: string; } export type RoutesOptions = Partial; /** * @private */ export interface AuthClientOptions { transactionStore: TransactionStore; sessionStore: AbstractSessionStore; domain: string; /** * Issuer URL override. When provided, this is used instead of constructing * the issuer from the domain hostname. Required for providers like Okta that * use path-based authorization server URLs (e.g. https://myorg.okta.com/oauth2/default/). */ issuer?: string; clientId: string; clientSecret?: string; clientAssertionSigningKey?: string | jose.CryptoKey; clientAssertionSigningAlg?: string; authorizationParameters?: AuthorizationParameters; pushedAuthorizationRequests?: boolean; secret: string; /** * Normalized appBaseUrl. When omitted, the SDK infers the base URL from the request. * If you construct AuthClient directly, normalize the value first. */ appBaseUrl?: string | string[]; signInReturnToPath?: string; logoutStrategy?: LogoutStrategy; includeIdTokenHintInOIDCLogoutUrl?: boolean; beforeSessionSaved?: BeforeSessionSavedHook; onCallback?: OnCallbackHook; routes: Routes; // custom fetch implementation to allow for dependency injection fetch?: typeof fetch; discoveryCache?: DiscoveryCache; provider?: AuthClientProvider; allowInsecureRequests?: boolean; httpTimeout?: number; enableTelemetry?: boolean; enableAccessTokenEndpoint?: boolean; noContentProfileResponseWhenUnauthenticated?: boolean; enableConnectAccountEndpoint?: boolean; tokenRefreshBuffer?: number; useDPoP?: boolean; dpopKeyPair?: DpopKeyPair; dpopOptions?: DpopOptions; /** * Enable mTLS (Mutual TLS) client authentication (RFC 8705). * * When `true`, the SDK uses `oauth.TlsClientAuth()` for client authentication * and routes all token requests to the mTLS endpoint aliases advertised in * the Auth0 discovery document (`mtls_endpoint_aliases`). * * Requires the `fetch` option to be set with a TLS-aware implementation * (e.g. Node.js `undici` with a client certificate). The standard `fetch` * global has no client certificate API. * * @default false */ useMtls?: boolean; /** * MFA token TTL in seconds (for token encryption expiration). * Default: 300 (5 minutes, matching Auth0's mfa_token expiration) */ mfaTokenTtl?: number; /** * Content Security Policy nonce for inline scripts. * Required when CSP is enabled and popup flows use postMessage return strategy. * The nonce is injected into the