@puzzle-ui/fullstack 0.0.0-stage → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +65 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/auth/client/auth.css +206 -0
  4. package/dist/auth/client/experience.cjs +340 -0
  5. package/dist/auth/client/experience.cjs.map +1 -0
  6. package/dist/auth/client/experience.d.cts +67 -0
  7. package/dist/auth/client/experience.d.ts +67 -0
  8. package/dist/auth/client/experience.js +340 -0
  9. package/dist/auth/client/experience.js.map +1 -0
  10. package/dist/auth/client/index.cjs +234 -0
  11. package/dist/auth/client/index.cjs.map +1 -0
  12. package/dist/auth/client/index.d.cts +48 -0
  13. package/dist/auth/client/index.d.ts +48 -0
  14. package/dist/auth/client/index.js +216 -0
  15. package/dist/auth/client/index.js.map +1 -0
  16. package/dist/auth/client/messages.cjs +80 -0
  17. package/dist/auth/client/messages.cjs.map +1 -0
  18. package/dist/auth/client/messages.d.cts +92 -0
  19. package/dist/auth/client/messages.d.ts +92 -0
  20. package/dist/auth/client/messages.js +79 -0
  21. package/dist/auth/client/messages.js.map +1 -0
  22. package/dist/auth/client/passkeys.cjs +43 -0
  23. package/dist/auth/client/passkeys.cjs.map +1 -0
  24. package/dist/auth/client/passkeys.d.cts +23 -0
  25. package/dist/auth/client/passkeys.d.ts +23 -0
  26. package/dist/auth/client/passkeys.js +40 -0
  27. package/dist/auth/client/passkeys.js.map +1 -0
  28. package/dist/auth/client/react.cjs +859 -0
  29. package/dist/auth/client/react.cjs.map +1 -0
  30. package/dist/auth/client/react.d.cts +72 -0
  31. package/dist/auth/client/react.d.ts +72 -0
  32. package/dist/auth/client/react.js +850 -0
  33. package/dist/auth/client/react.js.map +1 -0
  34. package/dist/auth/next/index.cjs +40 -0
  35. package/dist/auth/next/index.cjs.map +1 -0
  36. package/dist/auth/next/index.d.cts +12 -0
  37. package/dist/auth/next/index.d.ts +12 -0
  38. package/dist/auth/next/index.js +38 -0
  39. package/dist/auth/next/index.js.map +1 -0
  40. package/dist/auth/server/health.cjs +51 -0
  41. package/dist/auth/server/health.cjs.map +1 -0
  42. package/dist/auth/server/health.d.cts +15 -0
  43. package/dist/auth/server/health.d.ts +15 -0
  44. package/dist/auth/server/health.js +51 -0
  45. package/dist/auth/server/health.js.map +1 -0
  46. package/dist/auth/server/index.cjs +108 -0
  47. package/dist/auth/server/index.cjs.map +1 -0
  48. package/dist/auth/server/index.d.cts +22 -0
  49. package/dist/auth/server/index.d.ts +22 -0
  50. package/dist/auth/server/index.js +88 -0
  51. package/dist/auth/server/index.js.map +1 -0
  52. package/dist/auth/server/lifecycle.cjs +126 -0
  53. package/dist/auth/server/lifecycle.cjs.map +1 -0
  54. package/dist/auth/server/lifecycle.d.cts +20 -0
  55. package/dist/auth/server/lifecycle.d.ts +20 -0
  56. package/dist/auth/server/lifecycle.js +126 -0
  57. package/dist/auth/server/lifecycle.js.map +1 -0
  58. package/dist/auth/server/mfa.cjs +682 -0
  59. package/dist/auth/server/mfa.cjs.map +1 -0
  60. package/dist/auth/server/mfa.d.cts +90 -0
  61. package/dist/auth/server/mfa.d.ts +90 -0
  62. package/dist/auth/server/mfa.js +673 -0
  63. package/dist/auth/server/mfa.js.map +1 -0
  64. package/dist/auth/server/session.cjs +58 -0
  65. package/dist/auth/server/session.cjs.map +1 -0
  66. package/dist/auth/server/session.d.cts +14 -0
  67. package/dist/auth/server/session.d.ts +14 -0
  68. package/dist/auth/server/session.js +53 -0
  69. package/dist/auth/server/session.js.map +1 -0
  70. package/dist/auth/shared/index.cjs +0 -0
  71. package/dist/auth/shared/index.d.cts +64 -0
  72. package/dist/auth/shared/index.d.ts +64 -0
  73. package/dist/auth/shared/index.js +1 -0
  74. package/dist/auth/tanstack/index.cjs +34 -0
  75. package/dist/auth/tanstack/index.cjs.map +1 -0
  76. package/dist/auth/tanstack/index.d.cts +7 -0
  77. package/dist/auth/tanstack/index.d.ts +7 -0
  78. package/dist/auth/tanstack/index.js +32 -0
  79. package/dist/auth/tanstack/index.js.map +1 -0
  80. package/package.json +113 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa.cjs","names":["AuthError","AuthValidationError","AuthMfaChallengeError","readSessionCookie","AuthSessionError","serializeClearedSessionCookie","createSessionResponse","clearSessionResponse","getRequestSession"],"sources":["../../../src/auth/server/mfa.ts"],"sourcesContent":["import {\n AuthError,\n AuthMfaChallengeError,\n AuthSessionError,\n AuthValidationError,\n type AuthManager,\n type AuthMfaEnrollmentCompleted,\n type AuthMfaEnrollmentPendingResult,\n type AuthMfaPendingResult,\n type AuthResult,\n type AuthUser,\n type StorageAuthMfaManager,\n type StorageAuthStepUpManager,\n} from \"@puzzle-ui/auth\";\n\nimport {\n protectStateChangingRequest,\n readSessionCookie,\n serializeClearedSessionCookie,\n type CsrfOptions,\n type SessionCookieOptions,\n type SessionManager,\n type SessionRecord,\n} from \"@puzzle-ui/session\";\n\nimport {\n createSessionResponse,\n clearSessionResponse,\n getRequestSession,\n type SessionRequestOptions,\n} from \"./session\";\n\nexport const MFA_CHALLENGE_COOKIE_NAME = \"__Host-puzzle.mfa\";\n\nconst MAX_MFA_COOKIE_LIFETIME_SECONDS = 600;\n\nexport type AuthMfaHandlersOptions = {\n readonly auth: AuthManager;\n readonly mfa: StorageAuthMfaManager;\n readonly stepUp: StorageAuthStepUpManager;\n readonly sessions: SessionManager;\n readonly cookie: SessionCookieOptions;\n readonly csrf: CsrfOptions;\n\n /**\n * Resolve the client IP using the deployment's trusted proxy configuration.\n *\n * Do not blindly trust arbitrary X-Forwarded-For values.\n */\n readonly resolveClientIp: (request: Request) => string;\n};\n\nexport type AuthMfaPendingResponse = {\n readonly status: \"mfa-pending\";\n readonly purpose: \"sign-in\" | \"step-up\";\n readonly challengeId: string;\n readonly expiresAt: string;\n readonly availableFactors: readonly string[];\n};\n\nexport type AuthClientUserResponse = {\n readonly id: string;\n readonly email: string;\n readonly name: string | null;\n readonly image: string | null;\n readonly emailVerified: boolean;\n};\n\nexport type AuthMfaHandlers = {\n readonly register: (request: Request) => Promise<Response>;\n readonly signIn: (request: Request) => Promise<Response>;\n readonly getAuthState: (request: Request) => Promise<Response>;\n readonly signOut: (request: Request) => Promise<Response>;\n\n readonly verifySignInTotp: (request: Request) => Promise<Response>;\n readonly verifySignInRecoveryCode: (request: Request) => Promise<Response>;\n readonly beginPasskeySignIn: (request: Request) => Promise<Response>;\n readonly finishPasskeySignIn: (request: Request) => Promise<Response>;\n\n readonly beginPasskeyMfa: (request: Request) => Promise<Response>;\n readonly finishPasskeyMfa: (request: Request) => Promise<Response>;\n\n readonly beginRequiredTotpEnrollment: (request: Request) => Promise<Response>;\n readonly verifyRequiredTotpEnrollment: (\n request: Request,\n ) => Promise<Response>;\n readonly beginRequiredPasskeyEnrollment: (\n request: Request,\n ) => Promise<Response>;\n readonly finishRequiredPasskeyEnrollment: (\n request: Request,\n ) => Promise<Response>;\n\n readonly beginTotpEnrollment: (request: Request) => Promise<Response>;\n readonly verifyTotpEnrollment: (request: Request) => Promise<Response>;\n readonly beginPasskeyRegistration: (request: Request) => Promise<Response>;\n readonly finishPasskeyRegistration: (request: Request) => Promise<Response>;\n\n readonly listMfaFactors: (request: Request) => Promise<Response>;\n readonly removeMfaFactor: (request: Request) => Promise<Response>;\n readonly regenerateRecoveryCodes: (request: Request) => Promise<Response>;\n\n readonly beginStepUp: (request: Request) => Promise<Response>;\n readonly beginStepUpPasskey: (request: Request) => Promise<Response>;\n readonly verifyStepUpTotp: (request: Request) => Promise<Response>;\n readonly verifyStepUpRecoveryCode: (request: Request) => Promise<Response>;\n readonly finishStepUpPasskey: (request: Request) => Promise<Response>;\n};\n\ntype AuthEndpointHandler = (request: Request) => Promise<Response>;\n\ntype JsonObject = Record<string, unknown>;\n\ntype SignInResult = Awaited<ReturnType<AuthManager[\"signIn\"]>>;\n\ntype AuthFlowResult =\n | SignInResult\n | Awaited<ReturnType<AuthManager[\"register\"]>>;\n\nconst isMfaEnrollmentPendingResult = (\n result: AuthFlowResult,\n): result is AuthMfaEnrollmentPendingResult =>\n \"status\" in result && result.status === \"mfa-enrollment-pending\";\n\nconst isRecord = (value: unknown): value is JsonObject =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isMfaPendingResult = (\n result: SignInResult,\n): result is AuthMfaPendingResult =>\n \"status\" in result && result.status === \"mfa-pending\";\n\nconst jsonResponse = (\n value: unknown,\n status = 200,\n additionalHeaders?: HeadersInit,\n): Response => {\n const headers = new Headers(additionalHeaders);\n\n headers.set(\"Content-Type\", \"application/json; charset=utf-8\");\n headers.set(\"Cache-Control\", \"no-store\");\n headers.set(\"Pragma\", \"no-cache\");\n\n return new Response(JSON.stringify(value), {\n status,\n headers,\n });\n};\n\nconst toClientUser = (user: AuthUser): AuthClientUserResponse => ({\n id: user.id,\n email: user.email,\n name: user.name,\n image: user.image,\n emailVerified: user.emailVerifiedAt !== null,\n});\n\nconst containsChallengeToken = (value: unknown): boolean => {\n if (Array.isArray(value)) {\n return value.some(containsChallengeToken);\n }\n\n if (!isRecord(value)) {\n return false;\n }\n\n return Object.entries(value).some(\n ([key, nested]) =>\n key === \"challengeToken\" || containsChallengeToken(nested),\n );\n};\n\nconst getErrorResponse = (error: unknown): Response => {\n if (error instanceof AuthError) {\n const status =\n error.code === \"RATE_LIMITED\"\n ? 429\n : error.code === \"INVALID_CREDENTIALS\" ||\n error.code === \"SESSION\" ||\n error.code === \"MFA_CHALLENGE\"\n ? 401\n : error.code === \"PROVIDER\"\n ? 503\n : 400;\n\n return jsonResponse(\n {\n error: {\n code: error.code,\n message: error.message,\n },\n },\n status,\n );\n }\n\n if (error instanceof Error && error.name === \"AuthStepUpRequiredError\") {\n return jsonResponse(\n {\n error: {\n code: \"STEP_UP_REQUIRED\",\n message: \"Recent MFA verification is required.\",\n },\n },\n 403,\n );\n }\n\n if (\n error instanceof Error &&\n (error.name === \"SessionSecurityError\" ||\n error.name === \"SessionConfigurationError\")\n ) {\n return jsonResponse(\n {\n error: {\n code: \"REQUEST_FORBIDDEN\",\n message: \"The request could not be authorized.\",\n },\n },\n 403,\n );\n }\n\n return jsonResponse(\n {\n error: {\n code: \"INTERNAL_AUTH_ERROR\",\n message: \"An authentication error occurred.\",\n },\n },\n 500,\n );\n};\n\nconst endpoint =\n (handler: AuthEndpointHandler): AuthEndpointHandler =>\n async (request) => {\n try {\n return await handler(request);\n } catch (error) {\n return getErrorResponse(error);\n }\n };\n\nconst readJsonObject = async (request: Request): Promise<JsonObject> => {\n let value: unknown;\n\n try {\n value = await request.json();\n } catch {\n throw new AuthValidationError(\"A valid JSON request body is required.\");\n }\n\n if (!isRecord(value)) {\n throw new AuthValidationError(\"The request body must be a JSON object.\");\n }\n\n if (containsChallengeToken(value)) {\n throw new AuthValidationError(\n \"MFA challenge tokens are managed by the HttpOnly cookie.\",\n );\n }\n\n return value;\n};\n\nconst requiredString = (\n body: JsonObject,\n name: string,\n maximumLength = 2048,\n): string => {\n const value = body[name];\n\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0 ||\n value.length > maximumLength\n ) {\n throw new AuthValidationError(`A valid ${name} value is required.`);\n }\n\n return value;\n};\n\nconst optionalString = (\n body: JsonObject,\n name: string,\n maximumLength = 2048,\n): string | undefined => {\n const value = body[name];\n\n if (value === undefined || value === null || value === \"\") {\n return undefined;\n }\n\n if (typeof value !== \"string\" || value.length > maximumLength) {\n throw new AuthValidationError(`The ${name} value is invalid.`);\n }\n\n return value;\n};\n\nconst requireRequestMethod = (\n request: Request,\n expected: \"GET\" | \"POST\",\n): void => {\n if (request.method.toUpperCase() !== expected) {\n throw new AuthValidationError(\n `This authentication endpoint requires ${expected}.`,\n );\n }\n};\n\nconst protectPost = (\n request: Request,\n options: AuthMfaHandlersOptions,\n): void => {\n requireRequestMethod(request, \"POST\");\n protectStateChangingRequest(request, options.csrf);\n};\n\nconst requireSignInChallengeToken = (\n request: Request,\n options: AuthMfaHandlersOptions,\n): string => {\n const sessionToken = getSessionToken(request, options.cookie);\n\n if (sessionToken) {\n throw new AuthMfaChallengeError();\n }\n\n return requireMfaChallengeToken(request);\n};\n\nconst requireMfaChallengeToken = (request: Request): string => {\n const token = readMfaChallengeCookie(request.headers.get(\"cookie\"));\n\n if (!token) {\n throw new AuthMfaChallengeError();\n }\n\n return token;\n};\n\nconst getSessionToken = (\n request: Request,\n cookie: SessionCookieOptions,\n): string | null =>\n readSessionCookie(request.headers.get(\"cookie\"), cookie.name);\n\nconst getAuthenticatedContext = async (\n request: Request,\n options: AuthMfaHandlersOptions,\n): Promise<{\n readonly session: SessionRecord;\n readonly sessionToken: string;\n}> => {\n const sessionToken = getSessionToken(request, options.cookie);\n\n if (!sessionToken) {\n throw new AuthSessionError(\"An authenticated session is required.\");\n }\n\n let session: SessionRecord | null;\n\n try {\n session = await options.sessions.validate(sessionToken);\n } catch (error) {\n throw new AuthSessionError(\n \"Could not validate the authentication session.\",\n { cause: error },\n );\n }\n\n if (\n !session ||\n session.kind !== \"authenticated\" ||\n !session.userId ||\n session.revokedAt !== null ||\n session.expiresAt.getTime() <= Date.now()\n ) {\n throw new AuthSessionError(\"An authenticated session is required.\");\n }\n\n return {\n session,\n sessionToken,\n };\n};\n\nconst getClientIp = (\n request: Request,\n options: AuthMfaHandlersOptions,\n): string => {\n const ipAddress = options.resolveClientIp(request);\n\n if (\n typeof ipAddress !== \"string\" ||\n ipAddress.trim().length === 0 ||\n ipAddress.length > 128\n ) {\n throw new AuthValidationError(\n \"The request does not contain a valid client IP address.\",\n );\n }\n\n return ipAddress.trim();\n};\n\nconst withSetCookie = (response: Response, cookie: string): Response => {\n const headers = new Headers(response.headers);\n\n headers.append(\"Set-Cookie\", cookie);\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n};\n\nconst withClearedMfaChallengeCookie = (response: Response): Response =>\n withSetCookie(response, serializeClearedMfaChallengeCookie());\n\nconst withClearedSessionCookie = (\n response: Response,\n cookie: SessionCookieOptions,\n): Response => withSetCookie(response, serializeClearedSessionCookie(cookie));\n\nconst maxAgeUntil = (expiresAt: Date): number => {\n const seconds = Math.ceil((expiresAt.getTime() - Date.now()) / 1000);\n\n if (!Number.isFinite(seconds) || seconds <= 0) {\n throw new AuthMfaChallengeError();\n }\n\n return Math.min(seconds, MAX_MFA_COOKIE_LIFETIME_SECONDS);\n};\n\nconst createSessionResultResponse = (\n result: AuthResult,\n options: AuthMfaHandlersOptions,\n): Response => {\n const response = createSessionResponse(\n JSON.stringify({\n status: \"authenticated\",\n user: toClientUser(result.user),\n }),\n result.token,\n {\n sessions: options.sessions,\n cookie: options.cookie,\n },\n result.session.expiresAt,\n );\n\n return withClearedMfaChallengeCookie(response);\n};\n\nconst createMfaEnrollmentPendingResponse = (\n challenge: AuthMfaEnrollmentPendingResult,\n): Response =>\n createMfaChallengeResponse(challenge.challengeToken, challenge.expiresAt, {\n status: \"mfa-enrollment-pending\",\n purpose: \"enrollment\",\n challengeId: challenge.challengeId,\n expiresAt: challenge.expiresAt.toISOString(),\n availableFactors: challenge.availableFactors,\n });\n\nconst createEnrollmentSessionResultResponse = (\n completed: AuthMfaEnrollmentCompleted,\n options: AuthMfaHandlersOptions,\n): Response => {\n const response = createSessionResponse(\n JSON.stringify({\n status: \"mfa-factor-added\",\n factor: completed.factor,\n recoveryCodes: completed.recoveryCodes,\n }),\n completed.auth.token,\n {\n sessions: options.sessions,\n cookie: options.cookie,\n },\n completed.auth.session.expiresAt,\n );\n\n return withClearedMfaChallengeCookie(response);\n};\n\nexport const serializeMfaChallengeCookie = (\n challengeToken: string,\n maxAgeSeconds: number,\n): string => {\n if (\n typeof challengeToken !== \"string\" ||\n !/^[a-f0-9]{64}$/i.test(challengeToken)\n ) {\n throw new AuthMfaChallengeError();\n }\n\n if (\n !Number.isInteger(maxAgeSeconds) ||\n maxAgeSeconds < 1 ||\n maxAgeSeconds > MAX_MFA_COOKIE_LIFETIME_SECONDS\n ) {\n throw new AuthValidationError(\n \"The MFA challenge cookie lifetime is invalid.\",\n );\n }\n\n return [\n `${MFA_CHALLENGE_COOKIE_NAME}=${encodeURIComponent(challengeToken)}`,\n \"Path=/\",\n \"HttpOnly\",\n \"Secure\",\n \"SameSite=Strict\",\n `Max-Age=${maxAgeSeconds}`,\n ].join(\"; \");\n};\n\nexport const serializeClearedMfaChallengeCookie = (): string =>\n [\n `${MFA_CHALLENGE_COOKIE_NAME}=`,\n \"Path=/\",\n \"HttpOnly\",\n \"Secure\",\n \"SameSite=Strict\",\n \"Max-Age=0\",\n \"Expires=Thu, 01 Jan 1970 00:00:00 GMT\",\n ].join(\"; \");\n\nexport const readMfaChallengeCookie = (\n cookieHeader: string | null,\n): string | null => {\n if (!cookieHeader) {\n return null;\n }\n\n for (const part of cookieHeader.split(\";\")) {\n const separator = part.indexOf(\"=\");\n\n if (separator < 0) {\n continue;\n }\n\n const name = part.slice(0, separator).trim();\n\n if (name !== MFA_CHALLENGE_COOKIE_NAME) {\n continue;\n }\n\n let token: string;\n\n try {\n token = decodeURIComponent(part.slice(separator + 1).trim());\n } catch {\n return null;\n }\n\n return /^[a-f0-9]{64}$/i.test(token) ? token : null;\n }\n\n return null;\n};\n\n/**\n * Builds a safe public MFA-pending response.\n *\n * The raw challenge token is written only to Set-Cookie and is deliberately\n * excluded from the JSON body.\n */\nexport const createMfaPendingResponse = (\n challenge: AuthMfaPendingResult,\n purpose: \"sign-in\" | \"step-up\" = \"sign-in\",\n): Response =>\n createMfaChallengeResponse(challenge.challengeToken, challenge.expiresAt, {\n status: \"mfa-pending\",\n purpose,\n challengeId: challenge.challengeId,\n expiresAt: challenge.expiresAt.toISOString(),\n availableFactors: challenge.availableFactors,\n });\n\nexport const createMfaChallengeResponse = (\n challengeToken: string,\n expiresAt: Date,\n body: JsonObject,\n status = 200,\n): Response => {\n if (containsChallengeToken(body)) {\n throw new AuthValidationError(\n \"The raw MFA challenge token cannot be included in a response body.\",\n );\n }\n\n const headers = new Headers({\n \"Content-Type\": \"application/json; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n });\n\n headers.append(\n \"Set-Cookie\",\n serializeMfaChallengeCookie(challengeToken, maxAgeUntil(expiresAt)),\n );\n\n return new Response(JSON.stringify(body), {\n status,\n headers,\n });\n};\n\nexport const createAuthMfaHandlers = (\n options: AuthMfaHandlersOptions,\n): AuthMfaHandlers => {\n const sessionOptions: SessionRequestOptions = {\n sessions: options.sessions,\n cookie: options.cookie,\n };\n\n const register = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n\n const result = await options.auth.register({\n email: requiredString(body, \"email\", 320),\n password: requiredString(body, \"password\", 4096),\n name: optionalString(body, \"name\", 128),\n ipAddress: getClientIp(request, options),\n });\n\n if (isMfaEnrollmentPendingResult(result)) {\n const pending = createMfaEnrollmentPendingResponse(result);\n\n return withClearedSessionCookie(pending, options.cookie);\n }\n\n return createSessionResultResponse(result, options);\n });\n\n const signIn = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n\n const result = await options.auth.signIn({\n email: requiredString(body, \"email\", 320),\n password: requiredString(body, \"password\", 4096),\n ipAddress: getClientIp(request, options),\n });\n\n if (isMfaEnrollmentPendingResult(result)) {\n const pending = createMfaEnrollmentPendingResponse(result);\n\n return withClearedSessionCookie(pending, options.cookie);\n }\n\n if (isMfaPendingResult(result)) {\n const pending = createMfaPendingResponse(result, \"sign-in\");\n\n return withClearedSessionCookie(pending, options.cookie);\n }\n\n return createSessionResultResponse(result, options);\n });\n\n const getAuthState = endpoint(async (request) => {\n requireRequestMethod(request, \"GET\");\n\n const challengeToken = readMfaChallengeCookie(\n request.headers.get(\"cookie\"),\n );\n const sessionToken = getSessionToken(request, options.cookie);\n\n if (challengeToken && sessionToken) {\n const stepUpState = await options.stepUp.getPendingAuthState(\n challengeToken,\n sessionToken,\n );\n\n if (stepUpState) {\n const availableFactors =\n await options.mfa.getAvailableFactors(challengeToken);\n\n return jsonResponse({\n status: \"step-up-pending\",\n user: null,\n mfa: {\n purpose: \"step-up\",\n challengeId: stepUpState.challengeId,\n expiresAt: stepUpState.expiresAt.toISOString(),\n availableFactors,\n },\n });\n }\n } else if (challengeToken) {\n const pending = await options.auth.getAuthState(challengeToken);\n\n if (pending.status === \"mfa-pending\") {\n const availableFactors =\n await options.mfa.getAvailableFactors(challengeToken);\n\n return jsonResponse({\n status: \"mfa-pending\",\n user: null,\n mfa: {\n purpose: \"sign-in\",\n challengeId: pending.challengeId,\n expiresAt: pending.expiresAt.toISOString(),\n availableFactors,\n },\n });\n }\n\n if (pending.status === \"mfa-enrollment-pending\") {\n return jsonResponse({\n status: \"mfa-enrollment-pending\",\n user: null,\n mfa: {\n purpose: \"enrollment\",\n challengeId: pending.challengeId,\n expiresAt: pending.expiresAt.toISOString(),\n availableFactors: pending.availableFactors,\n },\n });\n }\n }\n\n if (!sessionToken) {\n return jsonResponse({\n status: \"unauthenticated\",\n user: null,\n });\n }\n\n const state = await options.auth.getAuthState(sessionToken);\n\n if (state.status === \"mfa-enrollment-required\") {\n return jsonResponse({\n status: \"mfa-enrollment-required\",\n user: null,\n });\n }\n\n if (state.status !== \"authenticated\") {\n return jsonResponse({\n status: \"unauthenticated\",\n user: null,\n });\n }\n\n const user = await options.auth.getCurrentUser(sessionToken);\n\n if (!user) {\n return jsonResponse({\n status: \"unauthenticated\",\n user: null,\n });\n }\n\n return jsonResponse({\n status: \"authenticated\",\n user: toClientUser(user),\n });\n });\n\n const signOut = endpoint(async (request) => {\n protectPost(request, options);\n\n const sessionToken = getSessionToken(request, options.cookie);\n\n if (sessionToken) {\n await options.auth.signOut(sessionToken);\n }\n\n const response = clearSessionResponse(\n JSON.stringify({ success: true }),\n sessionOptions,\n );\n\n return withClearedMfaChallengeCookie(response);\n });\n\n const verifySignInTotp = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const result = await options.mfa.verifySignInTotp({\n challengeToken,\n code: requiredString(body, \"code\", 16),\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n const verifySignInRecoveryCode = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const result = await options.mfa.verifySignInRecoveryCode({\n challengeToken,\n code: requiredString(body, \"code\", 128),\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n const beginPasskeySignIn = endpoint(async (request) => {\n protectPost(request, options);\n\n const challenge = await options.mfa.beginPasskeySignIn({\n ipAddress: getClientIp(request, options),\n });\n\n return createMfaChallengeResponse(\n challenge.challengeToken,\n challenge.expiresAt,\n {\n status: \"mfa-pending\",\n purpose: \"passkey-sign-in\",\n challengeId: challenge.challengeId,\n expiresAt: challenge.expiresAt.toISOString(),\n options: challenge.options,\n },\n );\n });\n\n const finishPasskeySignIn = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const response = body.response;\n\n if (!isRecord(response)) {\n throw new AuthValidationError(\n \"A valid passkey authentication response is required.\",\n );\n }\n\n const result = await options.mfa.finishPasskeySignIn({\n challengeToken: requireMfaChallengeToken(request),\n response,\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n const beginPasskeyMfa = endpoint(async (request) => {\n protectPost(request, options);\n\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const started = await options.mfa.beginPasskeyMfa(\n challengeToken,\n getClientIp(request, options),\n );\n\n const pending = await options.auth.getAuthState(challengeToken);\n\n if (pending.status !== \"mfa-pending\") {\n throw new AuthMfaChallengeError();\n }\n\n return createMfaChallengeResponse(challengeToken, started.expiresAt, {\n status: \"mfa-pending\",\n purpose: \"sign-in\",\n challengeId: pending.challengeId,\n expiresAt: started.expiresAt.toISOString(),\n options: started.options,\n });\n });\n\n const finishPasskeyMfa = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const response = body.response;\n\n if (!isRecord(response)) {\n throw new AuthValidationError(\n \"A valid passkey authentication response is required.\",\n );\n }\n\n const result = await options.mfa.finishPasskeyMfa({\n challengeToken: await requireSignInChallengeToken(request, options),\n response,\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n const beginRequiredTotpEnrollment = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const enrollment = await options.mfa.beginRequiredTotpEnrollment({\n challengeToken,\n ipAddress: getClientIp(request, options),\n label: optionalString(body, \"label\", 64),\n });\n\n return createMfaChallengeResponse(challengeToken, enrollment.expiresAt, {\n status: \"totp-enrollment-pending\",\n purpose: \"enrollment\",\n factorId: enrollment.factorId,\n secret: enrollment.secret,\n otpauthUrl: enrollment.otpauthUrl,\n expiresAt: enrollment.expiresAt.toISOString(),\n });\n });\n\n const verifyRequiredTotpEnrollment = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const completed = await options.mfa.verifyRequiredTotpEnrollment({\n challengeToken,\n factorId: requiredString(body, \"factorId\", 256),\n code: requiredString(body, \"code\", 16),\n ipAddress: getClientIp(request, options),\n });\n\n return createEnrollmentSessionResultResponse(completed, options);\n });\n\n const beginRequiredPasskeyEnrollment = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const started = await options.mfa.beginRequiredPasskeyEnrollment({\n challengeToken,\n ipAddress: getClientIp(request, options),\n label: optionalString(body, \"label\", 64),\n });\n\n return createMfaChallengeResponse(challengeToken, started.expiresAt, {\n status: \"passkey-enrollment-pending\",\n purpose: \"enrollment\",\n challengeId: started.challengeId,\n expiresAt: started.expiresAt.toISOString(),\n options: started.options,\n });\n });\n\n const finishRequiredPasskeyEnrollment = endpoint(async (request) => {\n protectPost(request, options);\n\n const body = await readJsonObject(request);\n\n if (!isRecord(body.response)) {\n throw new AuthValidationError(\n \"A valid passkey registration response is required.\",\n );\n }\n\n const challengeToken = await requireSignInChallengeToken(request, options);\n\n const completed = await options.mfa.finishRequiredPasskeyEnrollment({\n challengeToken,\n response: body.response,\n ipAddress: getClientIp(request, options),\n });\n\n return createEnrollmentSessionResultResponse(completed, options);\n });\n\n const beginTotpEnrollment = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n const enrollment = await options.mfa.beginTotpEnrollment({\n sessionToken: context.sessionToken,\n currentPassword: requiredString(body, \"currentPassword\", 4096),\n ipAddress: getClientIp(request, options),\n label: optionalString(body, \"label\", 64),\n });\n\n return jsonResponse({\n status: \"totp-enrollment-pending\",\n factorId: enrollment.factorId,\n secret: enrollment.secret,\n otpauthUrl: enrollment.otpauthUrl,\n expiresAt: enrollment.expiresAt.toISOString(),\n });\n });\n\n const verifyTotpEnrollment = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n const completed = await options.mfa.verifyTotpEnrollment({\n sessionToken: context.sessionToken,\n factorId: requiredString(body, \"factorId\", 256),\n code: requiredString(body, \"code\", 16),\n ipAddress: getClientIp(request, options),\n });\n\n return jsonResponse({\n status: \"mfa-factor-added\",\n factor: completed.factor,\n recoveryCodes: completed.recoveryCodes,\n });\n });\n\n const beginPasskeyRegistration = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n const started = await options.mfa.beginPasskeyRegistration({\n sessionToken: context.sessionToken,\n currentPassword: requiredString(body, \"currentPassword\", 4096),\n ipAddress: getClientIp(request, options),\n label: optionalString(body, \"label\", 64),\n });\n\n return createMfaChallengeResponse(\n started.challengeToken,\n started.expiresAt,\n {\n status: \"passkey-registration-pending\",\n challengeId: started.challengeId,\n expiresAt: started.expiresAt.toISOString(),\n options: started.options,\n },\n );\n });\n\n const finishPasskeyRegistration = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n if (!isRecord(body.response)) {\n throw new AuthValidationError(\n \"A valid passkey registration response is required.\",\n );\n }\n\n const completed = await options.mfa.finishPasskeyRegistration({\n sessionToken: context.sessionToken,\n challengeToken: requireMfaChallengeToken(request),\n response: body.response,\n ipAddress: getClientIp(request, options),\n });\n\n return withClearedMfaChallengeCookie(\n jsonResponse({\n status: \"mfa-factor-added\",\n factor: completed.factor,\n recoveryCodes: completed.recoveryCodes,\n }),\n );\n });\n\n const listMfaFactors = endpoint(async (request) => {\n requireRequestMethod(request, \"GET\");\n\n const context = await getAuthenticatedContext(request, options);\n const factors = await options.mfa.listFactors(context.sessionToken);\n const recoveryCodeCount = await options.mfa.getRecoveryCodeCount(\n context.sessionToken,\n );\n\n return jsonResponse({\n factors,\n recoveryCodeCount,\n });\n });\n\n const removeMfaFactor = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n await options.mfa.removeFactor({\n sessionToken: context.sessionToken,\n factorId: requiredString(body, \"factorId\", 256),\n currentPassword: requiredString(body, \"currentPassword\", 4096),\n ipAddress: getClientIp(request, options),\n });\n\n return jsonResponse({ success: true });\n });\n\n const regenerateRecoveryCodes = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n const recoveryCodes = await options.mfa.regenerateRecoveryCodes({\n sessionToken: context.sessionToken,\n currentPassword: requiredString(body, \"currentPassword\", 4096),\n ipAddress: getClientIp(request, options),\n });\n\n return jsonResponse({\n status: \"recovery-codes-regenerated\",\n recoveryCodes,\n });\n });\n\n const beginStepUp = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n\n const challenge = await options.stepUp.beginStepUp({\n sessionToken: context.sessionToken,\n ipAddress: getClientIp(request, options),\n });\n\n return createMfaPendingResponse(challenge, \"step-up\");\n });\n\n const beginStepUpPasskey = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const challengeToken = requireMfaChallengeToken(request);\n\n const started = await options.stepUp.beginPasskey({\n challengeToken,\n sessionToken: context.sessionToken,\n ipAddress: getClientIp(request, options),\n });\n\n const pending = await options.stepUp.getPendingAuthState(\n challengeToken,\n context.sessionToken,\n );\n\n if (!pending) {\n throw new AuthMfaChallengeError();\n }\n\n return createMfaChallengeResponse(challengeToken, started.expiresAt, {\n status: \"step-up-pending\",\n purpose: \"step-up\",\n challengeId: pending.challengeId,\n expiresAt: started.expiresAt.toISOString(),\n options: started.options,\n });\n });\n\n const verifyStepUpTotp = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n const result = await options.stepUp.verifyTotp({\n challengeToken: requireMfaChallengeToken(request),\n sessionToken: context.sessionToken,\n code: requiredString(body, \"code\", 16),\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n const verifyStepUpRecoveryCode = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n const result = await options.stepUp.verifyRecoveryCode({\n challengeToken: requireMfaChallengeToken(request),\n sessionToken: context.sessionToken,\n code: requiredString(body, \"code\", 128),\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n const finishStepUpPasskey = endpoint(async (request) => {\n protectPost(request, options);\n\n const context = await getAuthenticatedContext(request, options);\n const body = await readJsonObject(request);\n\n if (!isRecord(body.response)) {\n throw new AuthValidationError(\n \"A valid passkey authentication response is required.\",\n );\n }\n\n const result = await options.stepUp.finishPasskey({\n challengeToken: requireMfaChallengeToken(request),\n sessionToken: context.sessionToken,\n response: body.response,\n ipAddress: getClientIp(request, options),\n });\n\n return createSessionResultResponse(result, options);\n });\n\n return {\n register,\n signIn,\n getAuthState,\n signOut,\n\n verifySignInTotp,\n verifySignInRecoveryCode,\n beginPasskeySignIn,\n finishPasskeySignIn,\n\n beginPasskeyMfa,\n finishPasskeyMfa,\n\n beginRequiredTotpEnrollment,\n verifyRequiredTotpEnrollment,\n beginRequiredPasskeyEnrollment,\n finishRequiredPasskeyEnrollment,\n\n beginTotpEnrollment,\n verifyTotpEnrollment,\n beginPasskeyRegistration,\n finishPasskeyRegistration,\n\n listMfaFactors,\n removeMfaFactor,\n regenerateRecoveryCodes,\n\n beginStepUp,\n beginStepUpPasskey,\n verifyStepUpTotp,\n verifyStepUpRecoveryCode,\n finishStepUpPasskey,\n };\n};\n\n/**\n * Returns true when the current authenticated session has a recent,\n * successfully verified step-up.\n *\n * This can be used by sensitive server actions after `getRequestSession()`.\n */\nexport const hasRecentStepUp = (\n session: SessionRecord | null,\n maxAgeSeconds = 300,\n now = new Date(),\n): boolean => {\n if (\n !session ||\n session.kind !== \"authenticated\" ||\n !session.userId ||\n !Number.isInteger(maxAgeSeconds) ||\n maxAgeSeconds < 1 ||\n maxAgeSeconds > 3600\n ) {\n return false;\n }\n\n const verifiedAt = session.metadata?.stepUpVerifiedAt;\n const previousSessionId = session.metadata?.stepUpOfSessionId;\n\n if (\n typeof verifiedAt !== \"string\" ||\n typeof previousSessionId !== \"string\" ||\n previousSessionId.length === 0\n ) {\n return false;\n }\n\n const timestamp = Date.parse(verifiedAt);\n\n if (!Number.isFinite(timestamp)) {\n return false;\n }\n\n const age = now.getTime() - timestamp;\n\n return age >= -30_000 && age <= maxAgeSeconds * 1000;\n};\n\nexport class AuthStepUpRequiredError extends Error {\n constructor() {\n super(\"Recent MFA verification is required.\");\n\n this.name = \"AuthStepUpRequiredError\";\n }\n}\n\n/**\n * Use this from server actions that require recent MFA verification.\n *\n * The returned session has already been validated by SessionManager.\n */\nexport const requireRecentStepUp = async (\n request: Request,\n options: SessionRequestOptions,\n maxAgeSeconds = 300,\n): Promise<SessionRecord> => {\n const session = await getRequestSession(request, options);\n\n if (!session || !hasRecentStepUp(session, maxAgeSeconds)) {\n throw new AuthStepUpRequiredError();\n }\n\n return session;\n};\n"],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { SessionRequestOptions } from "./session.cjs";
2
+ import { AuthManager, AuthMfaPendingResult, StorageAuthMfaManager, StorageAuthStepUpManager } from "@puzzle-ui/auth";
3
+ import { CsrfOptions, SessionCookieOptions, SessionManager, SessionRecord } from "@puzzle-ui/session";
4
+ export declare const MFA_CHALLENGE_COOKIE_NAME = "__Host-puzzle.mfa";
5
+ export type AuthMfaHandlersOptions = {
6
+ readonly auth: AuthManager;
7
+ readonly mfa: StorageAuthMfaManager;
8
+ readonly stepUp: StorageAuthStepUpManager;
9
+ readonly sessions: SessionManager;
10
+ readonly cookie: SessionCookieOptions;
11
+ readonly csrf: CsrfOptions;
12
+ /**
13
+ * Resolve the client IP using the deployment's trusted proxy configuration.
14
+ *
15
+ * Do not blindly trust arbitrary X-Forwarded-For values.
16
+ */
17
+ readonly resolveClientIp: (request: Request) => string;
18
+ };
19
+ export type AuthMfaPendingResponse = {
20
+ readonly status: "mfa-pending";
21
+ readonly purpose: "sign-in" | "step-up";
22
+ readonly challengeId: string;
23
+ readonly expiresAt: string;
24
+ readonly availableFactors: readonly string[];
25
+ };
26
+ export type AuthClientUserResponse = {
27
+ readonly id: string;
28
+ readonly email: string;
29
+ readonly name: string | null;
30
+ readonly image: string | null;
31
+ readonly emailVerified: boolean;
32
+ };
33
+ export type AuthMfaHandlers = {
34
+ readonly register: (request: Request) => Promise<Response>;
35
+ readonly signIn: (request: Request) => Promise<Response>;
36
+ readonly getAuthState: (request: Request) => Promise<Response>;
37
+ readonly signOut: (request: Request) => Promise<Response>;
38
+ readonly verifySignInTotp: (request: Request) => Promise<Response>;
39
+ readonly verifySignInRecoveryCode: (request: Request) => Promise<Response>;
40
+ readonly beginPasskeySignIn: (request: Request) => Promise<Response>;
41
+ readonly finishPasskeySignIn: (request: Request) => Promise<Response>;
42
+ readonly beginPasskeyMfa: (request: Request) => Promise<Response>;
43
+ readonly finishPasskeyMfa: (request: Request) => Promise<Response>;
44
+ readonly beginRequiredTotpEnrollment: (request: Request) => Promise<Response>;
45
+ readonly verifyRequiredTotpEnrollment: (request: Request) => Promise<Response>;
46
+ readonly beginRequiredPasskeyEnrollment: (request: Request) => Promise<Response>;
47
+ readonly finishRequiredPasskeyEnrollment: (request: Request) => Promise<Response>;
48
+ readonly beginTotpEnrollment: (request: Request) => Promise<Response>;
49
+ readonly verifyTotpEnrollment: (request: Request) => Promise<Response>;
50
+ readonly beginPasskeyRegistration: (request: Request) => Promise<Response>;
51
+ readonly finishPasskeyRegistration: (request: Request) => Promise<Response>;
52
+ readonly listMfaFactors: (request: Request) => Promise<Response>;
53
+ readonly removeMfaFactor: (request: Request) => Promise<Response>;
54
+ readonly regenerateRecoveryCodes: (request: Request) => Promise<Response>;
55
+ readonly beginStepUp: (request: Request) => Promise<Response>;
56
+ readonly beginStepUpPasskey: (request: Request) => Promise<Response>;
57
+ readonly verifyStepUpTotp: (request: Request) => Promise<Response>;
58
+ readonly verifyStepUpRecoveryCode: (request: Request) => Promise<Response>;
59
+ readonly finishStepUpPasskey: (request: Request) => Promise<Response>;
60
+ };
61
+ type JsonObject = Record<string, unknown>;
62
+ export declare const serializeMfaChallengeCookie: (challengeToken: string, maxAgeSeconds: number) => string;
63
+ export declare const serializeClearedMfaChallengeCookie: () => string;
64
+ export declare const readMfaChallengeCookie: (cookieHeader: string | null) => string | null;
65
+ /**
66
+ * Builds a safe public MFA-pending response.
67
+ *
68
+ * The raw challenge token is written only to Set-Cookie and is deliberately
69
+ * excluded from the JSON body.
70
+ */
71
+ export declare const createMfaPendingResponse: (challenge: AuthMfaPendingResult, purpose?: "sign-in" | "step-up") => Response;
72
+ export declare const createMfaChallengeResponse: (challengeToken: string, expiresAt: Date, body: JsonObject, status?: number) => Response;
73
+ export declare const createAuthMfaHandlers: (options: AuthMfaHandlersOptions) => AuthMfaHandlers;
74
+ /**
75
+ * Returns true when the current authenticated session has a recent,
76
+ * successfully verified step-up.
77
+ *
78
+ * This can be used by sensitive server actions after `getRequestSession()`.
79
+ */
80
+ export declare const hasRecentStepUp: (session: SessionRecord | null, maxAgeSeconds?: number, now?: Date) => boolean;
81
+ export declare class AuthStepUpRequiredError extends Error {
82
+ constructor();
83
+ }
84
+ /**
85
+ * Use this from server actions that require recent MFA verification.
86
+ *
87
+ * The returned session has already been validated by SessionManager.
88
+ */
89
+ export declare const requireRecentStepUp: (request: Request, options: SessionRequestOptions, maxAgeSeconds?: number) => Promise<SessionRecord>;
90
+ export {};
@@ -0,0 +1,90 @@
1
+ import { SessionRequestOptions } from "./session.js";
2
+ import { AuthManager, AuthMfaPendingResult, StorageAuthMfaManager, StorageAuthStepUpManager } from "@puzzle-ui/auth";
3
+ import { CsrfOptions, SessionCookieOptions, SessionManager, SessionRecord } from "@puzzle-ui/session";
4
+ export declare const MFA_CHALLENGE_COOKIE_NAME = "__Host-puzzle.mfa";
5
+ export type AuthMfaHandlersOptions = {
6
+ readonly auth: AuthManager;
7
+ readonly mfa: StorageAuthMfaManager;
8
+ readonly stepUp: StorageAuthStepUpManager;
9
+ readonly sessions: SessionManager;
10
+ readonly cookie: SessionCookieOptions;
11
+ readonly csrf: CsrfOptions;
12
+ /**
13
+ * Resolve the client IP using the deployment's trusted proxy configuration.
14
+ *
15
+ * Do not blindly trust arbitrary X-Forwarded-For values.
16
+ */
17
+ readonly resolveClientIp: (request: Request) => string;
18
+ };
19
+ export type AuthMfaPendingResponse = {
20
+ readonly status: "mfa-pending";
21
+ readonly purpose: "sign-in" | "step-up";
22
+ readonly challengeId: string;
23
+ readonly expiresAt: string;
24
+ readonly availableFactors: readonly string[];
25
+ };
26
+ export type AuthClientUserResponse = {
27
+ readonly id: string;
28
+ readonly email: string;
29
+ readonly name: string | null;
30
+ readonly image: string | null;
31
+ readonly emailVerified: boolean;
32
+ };
33
+ export type AuthMfaHandlers = {
34
+ readonly register: (request: Request) => Promise<Response>;
35
+ readonly signIn: (request: Request) => Promise<Response>;
36
+ readonly getAuthState: (request: Request) => Promise<Response>;
37
+ readonly signOut: (request: Request) => Promise<Response>;
38
+ readonly verifySignInTotp: (request: Request) => Promise<Response>;
39
+ readonly verifySignInRecoveryCode: (request: Request) => Promise<Response>;
40
+ readonly beginPasskeySignIn: (request: Request) => Promise<Response>;
41
+ readonly finishPasskeySignIn: (request: Request) => Promise<Response>;
42
+ readonly beginPasskeyMfa: (request: Request) => Promise<Response>;
43
+ readonly finishPasskeyMfa: (request: Request) => Promise<Response>;
44
+ readonly beginRequiredTotpEnrollment: (request: Request) => Promise<Response>;
45
+ readonly verifyRequiredTotpEnrollment: (request: Request) => Promise<Response>;
46
+ readonly beginRequiredPasskeyEnrollment: (request: Request) => Promise<Response>;
47
+ readonly finishRequiredPasskeyEnrollment: (request: Request) => Promise<Response>;
48
+ readonly beginTotpEnrollment: (request: Request) => Promise<Response>;
49
+ readonly verifyTotpEnrollment: (request: Request) => Promise<Response>;
50
+ readonly beginPasskeyRegistration: (request: Request) => Promise<Response>;
51
+ readonly finishPasskeyRegistration: (request: Request) => Promise<Response>;
52
+ readonly listMfaFactors: (request: Request) => Promise<Response>;
53
+ readonly removeMfaFactor: (request: Request) => Promise<Response>;
54
+ readonly regenerateRecoveryCodes: (request: Request) => Promise<Response>;
55
+ readonly beginStepUp: (request: Request) => Promise<Response>;
56
+ readonly beginStepUpPasskey: (request: Request) => Promise<Response>;
57
+ readonly verifyStepUpTotp: (request: Request) => Promise<Response>;
58
+ readonly verifyStepUpRecoveryCode: (request: Request) => Promise<Response>;
59
+ readonly finishStepUpPasskey: (request: Request) => Promise<Response>;
60
+ };
61
+ type JsonObject = Record<string, unknown>;
62
+ export declare const serializeMfaChallengeCookie: (challengeToken: string, maxAgeSeconds: number) => string;
63
+ export declare const serializeClearedMfaChallengeCookie: () => string;
64
+ export declare const readMfaChallengeCookie: (cookieHeader: string | null) => string | null;
65
+ /**
66
+ * Builds a safe public MFA-pending response.
67
+ *
68
+ * The raw challenge token is written only to Set-Cookie and is deliberately
69
+ * excluded from the JSON body.
70
+ */
71
+ export declare const createMfaPendingResponse: (challenge: AuthMfaPendingResult, purpose?: "sign-in" | "step-up") => Response;
72
+ export declare const createMfaChallengeResponse: (challengeToken: string, expiresAt: Date, body: JsonObject, status?: number) => Response;
73
+ export declare const createAuthMfaHandlers: (options: AuthMfaHandlersOptions) => AuthMfaHandlers;
74
+ /**
75
+ * Returns true when the current authenticated session has a recent,
76
+ * successfully verified step-up.
77
+ *
78
+ * This can be used by sensitive server actions after `getRequestSession()`.
79
+ */
80
+ export declare const hasRecentStepUp: (session: SessionRecord | null, maxAgeSeconds?: number, now?: Date) => boolean;
81
+ export declare class AuthStepUpRequiredError extends Error {
82
+ constructor();
83
+ }
84
+ /**
85
+ * Use this from server actions that require recent MFA verification.
86
+ *
87
+ * The returned session has already been validated by SessionManager.
88
+ */
89
+ export declare const requireRecentStepUp: (request: Request, options: SessionRequestOptions, maxAgeSeconds?: number) => Promise<SessionRecord>;
90
+ export {};