@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.js","names":[],"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,58 @@
1
+ let _puzzle_ui_session = require("@puzzle-ui/session");
2
+ // src/auth/server/session.ts
3
+ const getRequestSession = async (request, options) => {
4
+ const token = (0, _puzzle_ui_session.readSessionCookie)(request.headers.get("cookie"), options.cookie.name);
5
+ if (!token) return null;
6
+ return options.sessions.validate(token);
7
+ };
8
+ const protectAuthenticatedRequest = (request, csrf) => {
9
+ (0, _puzzle_ui_session.protectStateChangingRequest)(request, csrf);
10
+ };
11
+ const createSessionResponse = (body, token, options, expiresAt, status = 200) => {
12
+ const maxAge = Math.floor((expiresAt.getTime() - Date.now()) / 1e3);
13
+ if (maxAge <= 0) throw new Error("Cannot create a cookie for an expired session.");
14
+ const headers = new Headers({
15
+ "Cache-Control": "no-store",
16
+ Pragma: "no-cache"
17
+ });
18
+ headers.append("Set-Cookie", (0, _puzzle_ui_session.serializeSessionCookie)(token, options.cookie, maxAge));
19
+ return new Response(body, {
20
+ status,
21
+ headers
22
+ });
23
+ };
24
+ const clearSessionResponse = (body, options, status = 200) => {
25
+ const headers = new Headers({
26
+ "Cache-Control": "no-store",
27
+ Pragma: "no-cache"
28
+ });
29
+ headers.append("Set-Cookie", (0, _puzzle_ui_session.serializeClearedSessionCookie)(options.cookie));
30
+ return new Response(body, {
31
+ status,
32
+ headers
33
+ });
34
+ };
35
+ const logoutRequest = async (request, options) => {
36
+ protectAuthenticatedRequest(request, options.csrf);
37
+ const session = await getRequestSession(request, options);
38
+ if (session) await options.sessions.revoke(session.id);
39
+ return clearSessionResponse(JSON.stringify({ success: true }), options);
40
+ };
41
+ const logoutAllRequest = async (request, options) => {
42
+ protectAuthenticatedRequest(request, options.csrf);
43
+ const session = await getRequestSession(request, options);
44
+ if (!session || session.kind !== "authenticated" || !session.userId) return new Response(null, {
45
+ status: 401,
46
+ headers: { "Cache-Control": "no-store" }
47
+ });
48
+ await options.sessions.revokeAll(session.userId);
49
+ return clearSessionResponse(JSON.stringify({ success: true }), options);
50
+ };
51
+ exports.clearSessionResponse = clearSessionResponse;
52
+ exports.createSessionResponse = createSessionResponse;
53
+ exports.getRequestSession = getRequestSession;
54
+ exports.logoutAllRequest = logoutAllRequest;
55
+ exports.logoutRequest = logoutRequest;
56
+ exports.protectAuthenticatedRequest = protectAuthenticatedRequest;
57
+
58
+ //# sourceMappingURL=session.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.cjs","names":["readSessionCookie","serializeSessionCookie","serializeClearedSessionCookie"],"sources":["../../../src/auth/server/session.ts"],"sourcesContent":["import {\n protectStateChangingRequest,\n readSessionCookie,\n serializeClearedSessionCookie,\n serializeSessionCookie,\n type CsrfOptions,\n type SessionCookieOptions,\n type SessionManager,\n} from \"@puzzle-ui/session\";\n\nexport type SessionRequestOptions = {\n readonly sessions: SessionManager;\n readonly cookie: SessionCookieOptions;\n};\n\nexport type SessionResponseOptions = SessionRequestOptions & {\n readonly csrf: CsrfOptions;\n};\n\nexport const getRequestSession = async (\n request: Request,\n options: SessionRequestOptions,\n) => {\n const token = readSessionCookie(\n request.headers.get(\"cookie\"),\n options.cookie.name,\n );\n\n if (!token) return null;\n\n return options.sessions.validate(token);\n};\n\nexport const protectAuthenticatedRequest = (\n request: Request,\n csrf: CsrfOptions,\n): void => {\n protectStateChangingRequest(request, csrf);\n};\n\nexport const createSessionResponse = (\n body: BodyInit | null,\n token: string,\n options: SessionRequestOptions,\n expiresAt: Date,\n status = 200,\n): Response => {\n const maxAge = Math.floor((expiresAt.getTime() - Date.now()) / 1000);\n\n if (maxAge <= 0) {\n throw new Error(\"Cannot create a cookie for an expired session.\");\n }\n\n const headers = new Headers({\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n });\n\n headers.append(\n \"Set-Cookie\",\n serializeSessionCookie(token, options.cookie, maxAge),\n );\n\n return new Response(body, {\n status,\n headers,\n });\n};\n\nexport const clearSessionResponse = (\n body: BodyInit | null,\n options: SessionRequestOptions,\n status = 200,\n): Response => {\n const headers = new Headers({\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n });\n\n headers.append(\"Set-Cookie\", serializeClearedSessionCookie(options.cookie));\n\n return new Response(body, {\n status,\n headers,\n });\n};\n\nexport const logoutRequest = async (\n request: Request,\n options: SessionResponseOptions,\n): Promise<Response> => {\n protectAuthenticatedRequest(request, options.csrf);\n\n const session = await getRequestSession(request, options);\n\n if (session) {\n await options.sessions.revoke(session.id);\n }\n\n return clearSessionResponse(JSON.stringify({ success: true }), options);\n};\n\nexport const logoutAllRequest = async (\n request: Request,\n options: SessionResponseOptions,\n): Promise<Response> => {\n protectAuthenticatedRequest(request, options.csrf);\n\n const session = await getRequestSession(request, options);\n\n if (!session || session.kind !== \"authenticated\" || !session.userId) {\n return new Response(null, {\n status: 401,\n headers: {\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n await options.sessions.revokeAll(session.userId);\n\n return clearSessionResponse(JSON.stringify({ success: true }), options);\n};\n"],"mappings":";;AAmBA,MAAa,oBAAoB,OAC/B,SACA,YACG;CACH,MAAM,SAAA,GAAQA,mBAAAA,kBAAAA,CACZ,QAAQ,QAAQ,IAAI,QAAQ,GAC5B,QAAQ,OAAO,IACjB;CAEA,IAAI,CAAC,OAAO,OAAO;CAEnB,OAAO,QAAQ,SAAS,SAAS,KAAK;AACxC;AAEA,MAAa,+BACX,SACA,SACS;CACT,CAAA,GAAA,mBAAA,4BAAA,CAA4B,SAAS,IAAI;AAC3C;AAEA,MAAa,yBACX,MACA,OACA,SACA,WACA,SAAS,QACI;CACb,MAAM,SAAS,KAAK,OAAO,UAAU,QAAQ,IAAI,KAAK,IAAI,KAAK,GAAI;CAEnE,IAAI,UAAU,GACZ,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,UAAU,IAAI,QAAQ;EAC1B,iBAAiB;EACjB,QAAQ;CACV,CAAC;CAED,QAAQ,OACN,eAAA,GACAC,mBAAAA,uBAAAA,CAAuB,OAAO,QAAQ,QAAQ,MAAM,CACtD;CAEA,OAAO,IAAI,SAAS,MAAM;EACxB;EACA;CACF,CAAC;AACH;AAEA,MAAa,wBACX,MACA,SACA,SAAS,QACI;CACb,MAAM,UAAU,IAAI,QAAQ;EAC1B,iBAAiB;EACjB,QAAQ;CACV,CAAC;CAED,QAAQ,OAAO,eAAA,GAAcC,mBAAAA,8BAAAA,CAA8B,QAAQ,MAAM,CAAC;CAE1E,OAAO,IAAI,SAAS,MAAM;EACxB;EACA;CACF,CAAC;AACH;AAEA,MAAa,gBAAgB,OAC3B,SACA,YACsB;CACtB,4BAA4B,SAAS,QAAQ,IAAI;CAEjD,MAAM,UAAU,MAAM,kBAAkB,SAAS,OAAO;CAExD,IAAI,SACF,MAAM,QAAQ,SAAS,OAAO,QAAQ,EAAE;CAG1C,OAAO,qBAAqB,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC,GAAG,OAAO;AACxE;AAEA,MAAa,mBAAmB,OAC9B,SACA,YACsB;CACtB,4BAA4B,SAAS,QAAQ,IAAI;CAEjD,MAAM,UAAU,MAAM,kBAAkB,SAAS,OAAO;CAExD,IAAI,CAAC,WAAW,QAAQ,SAAS,mBAAmB,CAAC,QAAQ,QAC3D,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS,EACP,iBAAiB,WACnB;CACF,CAAC;CAGH,MAAM,QAAQ,SAAS,UAAU,QAAQ,MAAM;CAE/C,OAAO,qBAAqB,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC,GAAG,OAAO;AACxE"}
@@ -0,0 +1,14 @@
1
+ import { CsrfOptions, SessionCookieOptions, SessionManager } from "@puzzle-ui/session";
2
+ export type SessionRequestOptions = {
3
+ readonly sessions: SessionManager;
4
+ readonly cookie: SessionCookieOptions;
5
+ };
6
+ export type SessionResponseOptions = SessionRequestOptions & {
7
+ readonly csrf: CsrfOptions;
8
+ };
9
+ export declare const getRequestSession: (request: Request, options: SessionRequestOptions) => Promise<import("@puzzle-ui/session").SessionRecord | null>;
10
+ export declare const protectAuthenticatedRequest: (request: Request, csrf: CsrfOptions) => void;
11
+ export declare const createSessionResponse: (body: BodyInit | null, token: string, options: SessionRequestOptions, expiresAt: Date, status?: number) => Response;
12
+ export declare const clearSessionResponse: (body: BodyInit | null, options: SessionRequestOptions, status?: number) => Response;
13
+ export declare const logoutRequest: (request: Request, options: SessionResponseOptions) => Promise<Response>;
14
+ export declare const logoutAllRequest: (request: Request, options: SessionResponseOptions) => Promise<Response>;
@@ -0,0 +1,14 @@
1
+ import { CsrfOptions, SessionCookieOptions, SessionManager } from "@puzzle-ui/session";
2
+ export type SessionRequestOptions = {
3
+ readonly sessions: SessionManager;
4
+ readonly cookie: SessionCookieOptions;
5
+ };
6
+ export type SessionResponseOptions = SessionRequestOptions & {
7
+ readonly csrf: CsrfOptions;
8
+ };
9
+ export declare const getRequestSession: (request: Request, options: SessionRequestOptions) => Promise<import("@puzzle-ui/session").SessionRecord | null>;
10
+ export declare const protectAuthenticatedRequest: (request: Request, csrf: CsrfOptions) => void;
11
+ export declare const createSessionResponse: (body: BodyInit | null, token: string, options: SessionRequestOptions, expiresAt: Date, status?: number) => Response;
12
+ export declare const clearSessionResponse: (body: BodyInit | null, options: SessionRequestOptions, status?: number) => Response;
13
+ export declare const logoutRequest: (request: Request, options: SessionResponseOptions) => Promise<Response>;
14
+ export declare const logoutAllRequest: (request: Request, options: SessionResponseOptions) => Promise<Response>;
@@ -0,0 +1,53 @@
1
+ import { protectStateChangingRequest, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie } from "@puzzle-ui/session";
2
+ // src/auth/server/session.ts
3
+ const getRequestSession = async (request, options) => {
4
+ const token = readSessionCookie(request.headers.get("cookie"), options.cookie.name);
5
+ if (!token) return null;
6
+ return options.sessions.validate(token);
7
+ };
8
+ const protectAuthenticatedRequest = (request, csrf) => {
9
+ protectStateChangingRequest(request, csrf);
10
+ };
11
+ const createSessionResponse = (body, token, options, expiresAt, status = 200) => {
12
+ const maxAge = Math.floor((expiresAt.getTime() - Date.now()) / 1e3);
13
+ if (maxAge <= 0) throw new Error("Cannot create a cookie for an expired session.");
14
+ const headers = new Headers({
15
+ "Cache-Control": "no-store",
16
+ Pragma: "no-cache"
17
+ });
18
+ headers.append("Set-Cookie", serializeSessionCookie(token, options.cookie, maxAge));
19
+ return new Response(body, {
20
+ status,
21
+ headers
22
+ });
23
+ };
24
+ const clearSessionResponse = (body, options, status = 200) => {
25
+ const headers = new Headers({
26
+ "Cache-Control": "no-store",
27
+ Pragma: "no-cache"
28
+ });
29
+ headers.append("Set-Cookie", serializeClearedSessionCookie(options.cookie));
30
+ return new Response(body, {
31
+ status,
32
+ headers
33
+ });
34
+ };
35
+ const logoutRequest = async (request, options) => {
36
+ protectAuthenticatedRequest(request, options.csrf);
37
+ const session = await getRequestSession(request, options);
38
+ if (session) await options.sessions.revoke(session.id);
39
+ return clearSessionResponse(JSON.stringify({ success: true }), options);
40
+ };
41
+ const logoutAllRequest = async (request, options) => {
42
+ protectAuthenticatedRequest(request, options.csrf);
43
+ const session = await getRequestSession(request, options);
44
+ if (!session || session.kind !== "authenticated" || !session.userId) return new Response(null, {
45
+ status: 401,
46
+ headers: { "Cache-Control": "no-store" }
47
+ });
48
+ await options.sessions.revokeAll(session.userId);
49
+ return clearSessionResponse(JSON.stringify({ success: true }), options);
50
+ };
51
+ export { clearSessionResponse, createSessionResponse, getRequestSession, logoutAllRequest, logoutRequest, protectAuthenticatedRequest };
52
+
53
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","names":[],"sources":["../../../src/auth/server/session.ts"],"sourcesContent":["import {\n protectStateChangingRequest,\n readSessionCookie,\n serializeClearedSessionCookie,\n serializeSessionCookie,\n type CsrfOptions,\n type SessionCookieOptions,\n type SessionManager,\n} from \"@puzzle-ui/session\";\n\nexport type SessionRequestOptions = {\n readonly sessions: SessionManager;\n readonly cookie: SessionCookieOptions;\n};\n\nexport type SessionResponseOptions = SessionRequestOptions & {\n readonly csrf: CsrfOptions;\n};\n\nexport const getRequestSession = async (\n request: Request,\n options: SessionRequestOptions,\n) => {\n const token = readSessionCookie(\n request.headers.get(\"cookie\"),\n options.cookie.name,\n );\n\n if (!token) return null;\n\n return options.sessions.validate(token);\n};\n\nexport const protectAuthenticatedRequest = (\n request: Request,\n csrf: CsrfOptions,\n): void => {\n protectStateChangingRequest(request, csrf);\n};\n\nexport const createSessionResponse = (\n body: BodyInit | null,\n token: string,\n options: SessionRequestOptions,\n expiresAt: Date,\n status = 200,\n): Response => {\n const maxAge = Math.floor((expiresAt.getTime() - Date.now()) / 1000);\n\n if (maxAge <= 0) {\n throw new Error(\"Cannot create a cookie for an expired session.\");\n }\n\n const headers = new Headers({\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n });\n\n headers.append(\n \"Set-Cookie\",\n serializeSessionCookie(token, options.cookie, maxAge),\n );\n\n return new Response(body, {\n status,\n headers,\n });\n};\n\nexport const clearSessionResponse = (\n body: BodyInit | null,\n options: SessionRequestOptions,\n status = 200,\n): Response => {\n const headers = new Headers({\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n });\n\n headers.append(\"Set-Cookie\", serializeClearedSessionCookie(options.cookie));\n\n return new Response(body, {\n status,\n headers,\n });\n};\n\nexport const logoutRequest = async (\n request: Request,\n options: SessionResponseOptions,\n): Promise<Response> => {\n protectAuthenticatedRequest(request, options.csrf);\n\n const session = await getRequestSession(request, options);\n\n if (session) {\n await options.sessions.revoke(session.id);\n }\n\n return clearSessionResponse(JSON.stringify({ success: true }), options);\n};\n\nexport const logoutAllRequest = async (\n request: Request,\n options: SessionResponseOptions,\n): Promise<Response> => {\n protectAuthenticatedRequest(request, options.csrf);\n\n const session = await getRequestSession(request, options);\n\n if (!session || session.kind !== \"authenticated\" || !session.userId) {\n return new Response(null, {\n status: 401,\n headers: {\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n await options.sessions.revokeAll(session.userId);\n\n return clearSessionResponse(JSON.stringify({ success: true }), options);\n};\n"],"mappings":";;AAmBA,MAAa,oBAAoB,OAC/B,SACA,YACG;CACH,MAAM,QAAQ,kBACZ,QAAQ,QAAQ,IAAI,QAAQ,GAC5B,QAAQ,OAAO,IACjB;CAEA,IAAI,CAAC,OAAO,OAAO;CAEnB,OAAO,QAAQ,SAAS,SAAS,KAAK;AACxC;AAEA,MAAa,+BACX,SACA,SACS;CACT,4BAA4B,SAAS,IAAI;AAC3C;AAEA,MAAa,yBACX,MACA,OACA,SACA,WACA,SAAS,QACI;CACb,MAAM,SAAS,KAAK,OAAO,UAAU,QAAQ,IAAI,KAAK,IAAI,KAAK,GAAI;CAEnE,IAAI,UAAU,GACZ,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,UAAU,IAAI,QAAQ;EAC1B,iBAAiB;EACjB,QAAQ;CACV,CAAC;CAED,QAAQ,OACN,cACA,uBAAuB,OAAO,QAAQ,QAAQ,MAAM,CACtD;CAEA,OAAO,IAAI,SAAS,MAAM;EACxB;EACA;CACF,CAAC;AACH;AAEA,MAAa,wBACX,MACA,SACA,SAAS,QACI;CACb,MAAM,UAAU,IAAI,QAAQ;EAC1B,iBAAiB;EACjB,QAAQ;CACV,CAAC;CAED,QAAQ,OAAO,cAAc,8BAA8B,QAAQ,MAAM,CAAC;CAE1E,OAAO,IAAI,SAAS,MAAM;EACxB;EACA;CACF,CAAC;AACH;AAEA,MAAa,gBAAgB,OAC3B,SACA,YACsB;CACtB,4BAA4B,SAAS,QAAQ,IAAI;CAEjD,MAAM,UAAU,MAAM,kBAAkB,SAAS,OAAO;CAExD,IAAI,SACF,MAAM,QAAQ,SAAS,OAAO,QAAQ,EAAE;CAG1C,OAAO,qBAAqB,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC,GAAG,OAAO;AACxE;AAEA,MAAa,mBAAmB,OAC9B,SACA,YACsB;CACtB,4BAA4B,SAAS,QAAQ,IAAI;CAEjD,MAAM,UAAU,MAAM,kBAAkB,SAAS,OAAO;CAExD,IAAI,CAAC,WAAW,QAAQ,SAAS,mBAAmB,CAAC,QAAQ,QAC3D,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS,EACP,iBAAiB,WACnB;CACF,CAAC;CAGH,MAAM,QAAQ,SAAS,UAAU,QAAQ,MAAM;CAE/C,OAAO,qBAAqB,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC,GAAG,OAAO;AACxE"}
File without changes
@@ -0,0 +1,64 @@
1
+ export type AuthClientStatus = "loading" | "unauthenticated" | "mfa-pending" | "mfa-enrollment-pending" | "mfa-enrollment-required" | "step-up-pending" | "authenticated";
2
+ export type AuthClientUser = {
3
+ readonly id: string;
4
+ readonly email: string;
5
+ readonly name: string | null;
6
+ readonly image: string | null;
7
+ readonly emailVerified: boolean;
8
+ };
9
+ export type AuthClientMfaState = {
10
+ readonly purpose: "sign-in" | "enrollment" | "step-up" | "passkey-sign-in";
11
+ readonly challengeId: string;
12
+ readonly expiresAt: string;
13
+ readonly availableFactors?: readonly ("totp" | "passkey" | "recovery-code")[];
14
+ };
15
+ export type AuthClientOptions = {
16
+ readonly basePath?: string;
17
+ readonly credentials?: "same-origin" | "include";
18
+ readonly fetcher?: typeof fetch;
19
+ };
20
+ export type AuthClientSnapshot = {
21
+ readonly status: AuthClientStatus;
22
+ readonly user: AuthClientUser | null;
23
+ readonly mfa?: AuthClientMfaState;
24
+ };
25
+ export type AuthClientSignInInput = {
26
+ readonly email: string;
27
+ readonly password: string;
28
+ };
29
+ export type AuthClientRegisterInput = {
30
+ readonly email: string;
31
+ readonly password: string;
32
+ readonly name?: string | null;
33
+ };
34
+ export type AuthClientMfaFactor = {
35
+ readonly id: string;
36
+ readonly type: "totp" | "passkey";
37
+ readonly status: "pending" | "active";
38
+ readonly label: string;
39
+ readonly createdAt: string;
40
+ readonly lastUsedAt: string | null;
41
+ };
42
+ export type AuthClientTotpEnrollment = {
43
+ readonly status: "totp-enrollment-pending";
44
+ readonly factorId: string;
45
+ readonly secret: string;
46
+ readonly otpauthUrl: string;
47
+ readonly expiresAt: string;
48
+ };
49
+ export type AuthClientEnrollmentResult = {
50
+ readonly status: "mfa-factor-added";
51
+ readonly factor: AuthClientMfaFactor;
52
+ readonly recoveryCodes: readonly string[];
53
+ };
54
+ export type AuthClientRecoveryCodesResult = {
55
+ readonly status: "recovery-codes-regenerated";
56
+ readonly recoveryCodes: readonly string[];
57
+ };
58
+ export type AuthClientFactorList = {
59
+ readonly factors: readonly AuthClientMfaFactor[];
60
+ readonly recoveryCodeCount: number;
61
+ };
62
+ export type AuthClientLifecycleResult = {
63
+ readonly status: "verification-requested" | "email-verified" | "password-reset-requested" | "password-reset" | "password-changed";
64
+ };
@@ -0,0 +1,64 @@
1
+ export type AuthClientStatus = "loading" | "unauthenticated" | "mfa-pending" | "mfa-enrollment-pending" | "mfa-enrollment-required" | "step-up-pending" | "authenticated";
2
+ export type AuthClientUser = {
3
+ readonly id: string;
4
+ readonly email: string;
5
+ readonly name: string | null;
6
+ readonly image: string | null;
7
+ readonly emailVerified: boolean;
8
+ };
9
+ export type AuthClientMfaState = {
10
+ readonly purpose: "sign-in" | "enrollment" | "step-up" | "passkey-sign-in";
11
+ readonly challengeId: string;
12
+ readonly expiresAt: string;
13
+ readonly availableFactors?: readonly ("totp" | "passkey" | "recovery-code")[];
14
+ };
15
+ export type AuthClientOptions = {
16
+ readonly basePath?: string;
17
+ readonly credentials?: "same-origin" | "include";
18
+ readonly fetcher?: typeof fetch;
19
+ };
20
+ export type AuthClientSnapshot = {
21
+ readonly status: AuthClientStatus;
22
+ readonly user: AuthClientUser | null;
23
+ readonly mfa?: AuthClientMfaState;
24
+ };
25
+ export type AuthClientSignInInput = {
26
+ readonly email: string;
27
+ readonly password: string;
28
+ };
29
+ export type AuthClientRegisterInput = {
30
+ readonly email: string;
31
+ readonly password: string;
32
+ readonly name?: string | null;
33
+ };
34
+ export type AuthClientMfaFactor = {
35
+ readonly id: string;
36
+ readonly type: "totp" | "passkey";
37
+ readonly status: "pending" | "active";
38
+ readonly label: string;
39
+ readonly createdAt: string;
40
+ readonly lastUsedAt: string | null;
41
+ };
42
+ export type AuthClientTotpEnrollment = {
43
+ readonly status: "totp-enrollment-pending";
44
+ readonly factorId: string;
45
+ readonly secret: string;
46
+ readonly otpauthUrl: string;
47
+ readonly expiresAt: string;
48
+ };
49
+ export type AuthClientEnrollmentResult = {
50
+ readonly status: "mfa-factor-added";
51
+ readonly factor: AuthClientMfaFactor;
52
+ readonly recoveryCodes: readonly string[];
53
+ };
54
+ export type AuthClientRecoveryCodesResult = {
55
+ readonly status: "recovery-codes-regenerated";
56
+ readonly recoveryCodes: readonly string[];
57
+ };
58
+ export type AuthClientFactorList = {
59
+ readonly factors: readonly AuthClientMfaFactor[];
60
+ readonly recoveryCodeCount: number;
61
+ };
62
+ export type AuthClientLifecycleResult = {
63
+ readonly status: "verification-requested" | "email-verified" | "password-reset-requested" | "password-reset" | "password-changed";
64
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_auth_server_index = require("../server/index.cjs");
3
+ // src/auth/tanstack/index.ts
4
+ const createDispatcher = (routes) => async (request) => {
5
+ const pathname = new URL(request.url).pathname;
6
+ const method = request.method.toUpperCase();
7
+ const route = routes[pathname];
8
+ if (!route) return new Response(null, {
9
+ status: 404,
10
+ headers: { "Cache-Control": "no-store" }
11
+ });
12
+ if (method !== "GET" && method !== "POST") return new Response(null, {
13
+ status: 405,
14
+ headers: {
15
+ Allow: Object.keys(route).join(", "),
16
+ "Cache-Control": "no-store"
17
+ }
18
+ });
19
+ const handler = route[method];
20
+ if (!handler) return new Response(null, {
21
+ status: 405,
22
+ headers: {
23
+ Allow: Object.keys(route).join(", "),
24
+ "Cache-Control": "no-store"
25
+ }
26
+ });
27
+ return handler(request);
28
+ };
29
+ const createTanStackAuthRouteHandlers = (options) => require_auth_server_index.createAuthRouteHandlers(options.auth, options.basePath);
30
+ const createTanStackAuthHandler = (options) => createDispatcher(createTanStackAuthRouteHandlers(options));
31
+ exports.createTanStackAuthHandler = createTanStackAuthHandler;
32
+ exports.createTanStackAuthRouteHandlers = createTanStackAuthRouteHandlers;
33
+
34
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["createAuthRouteHandlers"],"sources":["../../../src/auth/tanstack/index.ts"],"sourcesContent":["import {\n createAuthRouteHandlers,\n type AuthRouteHandlerMap,\n type AuthServer,\n} from \"../server\";\n\nexport type TanStackAuthIntegrationOptions = {\n readonly auth: AuthServer;\n readonly basePath?: string;\n};\n\nconst createDispatcher =\n (routes: AuthRouteHandlerMap) =>\n async (request: Request): Promise<Response> => {\n const pathname = new URL(request.url).pathname;\n const method = request.method.toUpperCase();\n\n const route = routes[pathname];\n\n if (!route) {\n return new Response(null, {\n status: 404,\n headers: {\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n if (method !== \"GET\" && method !== \"POST\") {\n return new Response(null, {\n status: 405,\n headers: {\n Allow: Object.keys(route).join(\", \"),\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n const handler = route[method];\n\n if (!handler) {\n return new Response(null, {\n status: 405,\n headers: {\n Allow: Object.keys(route).join(\", \"),\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n return handler(request);\n };\n\nexport const createTanStackAuthRouteHandlers = (\n options: TanStackAuthIntegrationOptions,\n): AuthRouteHandlerMap =>\n createAuthRouteHandlers(options.auth, options.basePath);\n\nexport const createTanStackAuthHandler = (\n options: TanStackAuthIntegrationOptions,\n) => createDispatcher(createTanStackAuthRouteHandlers(options));\n"],"mappings":";;;AAWA,MAAM,oBACH,WACD,OAAO,YAAwC;CAC7C,MAAM,WAAW,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC;CACtC,MAAM,SAAS,QAAQ,OAAO,YAAY;CAE1C,MAAM,QAAQ,OAAO;CAErB,IAAI,CAAC,OACH,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS,EACP,iBAAiB,WACnB;CACF,CAAC;CAGH,IAAI,WAAW,SAAS,WAAW,QACjC,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS;GACP,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;GACnC,iBAAiB;EACnB;CACF,CAAC;CAGH,MAAM,UAAU,MAAM;CAEtB,IAAI,CAAC,SACH,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS;GACP,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;GACnC,iBAAiB;EACnB;CACF,CAAC;CAGH,OAAO,QAAQ,OAAO;AACxB;AAEF,MAAa,mCACX,YAEAA,0BAAAA,wBAAwB,QAAQ,MAAM,QAAQ,QAAQ;AAExD,MAAa,6BACX,YACG,iBAAiB,gCAAgC,OAAO,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { AuthRouteHandlerMap, AuthServer } from "../server/index.cjs";
2
+ export type TanStackAuthIntegrationOptions = {
3
+ readonly auth: AuthServer;
4
+ readonly basePath?: string;
5
+ };
6
+ export declare const createTanStackAuthRouteHandlers: (options: TanStackAuthIntegrationOptions) => AuthRouteHandlerMap;
7
+ export declare const createTanStackAuthHandler: (options: TanStackAuthIntegrationOptions) => (request: Request) => Promise<Response>;
@@ -0,0 +1,7 @@
1
+ import { AuthRouteHandlerMap, AuthServer } from "../server/index.js";
2
+ export type TanStackAuthIntegrationOptions = {
3
+ readonly auth: AuthServer;
4
+ readonly basePath?: string;
5
+ };
6
+ export declare const createTanStackAuthRouteHandlers: (options: TanStackAuthIntegrationOptions) => AuthRouteHandlerMap;
7
+ export declare const createTanStackAuthHandler: (options: TanStackAuthIntegrationOptions) => (request: Request) => Promise<Response>;
@@ -0,0 +1,32 @@
1
+ import { createAuthRouteHandlers } from "../server/index.js";
2
+ // src/auth/tanstack/index.ts
3
+ const createDispatcher = (routes) => async (request) => {
4
+ const pathname = new URL(request.url).pathname;
5
+ const method = request.method.toUpperCase();
6
+ const route = routes[pathname];
7
+ if (!route) return new Response(null, {
8
+ status: 404,
9
+ headers: { "Cache-Control": "no-store" }
10
+ });
11
+ if (method !== "GET" && method !== "POST") return new Response(null, {
12
+ status: 405,
13
+ headers: {
14
+ Allow: Object.keys(route).join(", "),
15
+ "Cache-Control": "no-store"
16
+ }
17
+ });
18
+ const handler = route[method];
19
+ if (!handler) return new Response(null, {
20
+ status: 405,
21
+ headers: {
22
+ Allow: Object.keys(route).join(", "),
23
+ "Cache-Control": "no-store"
24
+ }
25
+ });
26
+ return handler(request);
27
+ };
28
+ const createTanStackAuthRouteHandlers = (options) => createAuthRouteHandlers(options.auth, options.basePath);
29
+ const createTanStackAuthHandler = (options) => createDispatcher(createTanStackAuthRouteHandlers(options));
30
+ export { createTanStackAuthHandler, createTanStackAuthRouteHandlers };
31
+
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/auth/tanstack/index.ts"],"sourcesContent":["import {\n createAuthRouteHandlers,\n type AuthRouteHandlerMap,\n type AuthServer,\n} from \"../server\";\n\nexport type TanStackAuthIntegrationOptions = {\n readonly auth: AuthServer;\n readonly basePath?: string;\n};\n\nconst createDispatcher =\n (routes: AuthRouteHandlerMap) =>\n async (request: Request): Promise<Response> => {\n const pathname = new URL(request.url).pathname;\n const method = request.method.toUpperCase();\n\n const route = routes[pathname];\n\n if (!route) {\n return new Response(null, {\n status: 404,\n headers: {\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n if (method !== \"GET\" && method !== \"POST\") {\n return new Response(null, {\n status: 405,\n headers: {\n Allow: Object.keys(route).join(\", \"),\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n const handler = route[method];\n\n if (!handler) {\n return new Response(null, {\n status: 405,\n headers: {\n Allow: Object.keys(route).join(\", \"),\n \"Cache-Control\": \"no-store\",\n },\n });\n }\n\n return handler(request);\n };\n\nexport const createTanStackAuthRouteHandlers = (\n options: TanStackAuthIntegrationOptions,\n): AuthRouteHandlerMap =>\n createAuthRouteHandlers(options.auth, options.basePath);\n\nexport const createTanStackAuthHandler = (\n options: TanStackAuthIntegrationOptions,\n) => createDispatcher(createTanStackAuthRouteHandlers(options));\n"],"mappings":";;AAWA,MAAM,oBACH,WACD,OAAO,YAAwC;CAC7C,MAAM,WAAW,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC;CACtC,MAAM,SAAS,QAAQ,OAAO,YAAY;CAE1C,MAAM,QAAQ,OAAO;CAErB,IAAI,CAAC,OACH,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS,EACP,iBAAiB,WACnB;CACF,CAAC;CAGH,IAAI,WAAW,SAAS,WAAW,QACjC,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS;GACP,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;GACnC,iBAAiB;EACnB;CACF,CAAC;CAGH,MAAM,UAAU,MAAM;CAEtB,IAAI,CAAC,SACH,OAAO,IAAI,SAAS,MAAM;EACxB,QAAQ;EACR,SAAS;GACP,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;GACnC,iBAAiB;EACnB;CACF,CAAC;CAGH,OAAO,QAAQ,OAAO;AACxB;AAEF,MAAa,mCACX,YAEA,wBAAwB,QAAQ,MAAM,QAAQ,QAAQ;AAExD,MAAa,6BACX,YACG,iBAAiB,gCAAgC,OAAO,CAAC"}