@syncello/auth 2.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.
- package/README.md +187 -0
- package/dist/chunk-OZ26T3ZA.js +263 -0
- package/dist/chunk-OZ26T3ZA.js.map +1 -0
- package/dist/cli/index.js +1100 -0
- package/dist/index.cjs +5751 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1339 -0
- package/dist/index.d.ts +1339 -0
- package/dist/index.js +5359 -0
- package/dist/index.js.map +1 -0
- package/dist/schema/index.cjs +292 -0
- package/dist/schema/index.cjs.map +1 -0
- package/dist/schema/index.d.cts +671 -0
- package/dist/schema/index.d.ts +671 -0
- package/dist/schema/index.js +13 -0
- package/dist/schema/index.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/password.ts","../src/lib/logger.ts","../src/core/session.ts","../src/core/tokens.ts","../src/core/config.ts","../src/core/cookies.ts","../src/core/2fa/totp.ts","../src/core/2fa/backup-codes.ts","../src/core/2fa/trusted-devices.ts","../src/core/2fa/challenge.ts","../src/core/fingerprint.ts","../src/core/account-lockout.ts","../src/core/turnstile.ts","../src/core/email-change.ts","../src/core/api-keys.ts","../src/core/csrf.ts","../src/core/mobile.ts","../src/middleware/auth.ts","../src/factory.ts","../src/lib/problem-json.ts","../src/middleware/csrf.ts","../src/middleware/rateLimit.ts","../src/lib/rate-limit-kv.ts","../src/middleware/require-verified-email.ts","../src/routes/index.ts","../src/routes/signup.ts","../src/lib/email/email-service.ts","../src/lib/email/security.ts","../src/core/email-template.ts","../src/lib/email/templates.ts","../src/lib/email/adapters/resend-adapter.ts","../src/lib/email/adapters/factory.ts","../src/lib/email/resend-client.ts","../src/lib/email/webhook-handler.ts","../src/lib/email/monitoring.ts","../src/routes/schemas.ts","../src/routes/login.ts","../src/routes/2fa/helpers.ts","../src/routes/logout.ts","../src/routes/me.ts","../src/routes/verify-email.ts","../src/routes/forgot-password.ts","../src/core/password-reset.ts","../src/routes/reset-password.ts","../src/routes/change-password.ts","../src/core/change-password.ts","../src/routes/heartbeat.ts","../src/routes/change-email.ts","../src/routes/confirm-email-change.ts","../src/routes/cancel-email-change.ts","../src/routes/delete-account.ts","../src/routes/refresh.ts","../src/routes/resend-verification.ts","../src/routes/2fa/index.ts","../src/routes/2fa/status.ts","../src/routes/2fa/schemas.ts","../src/routes/2fa/totp-setup.ts","../src/routes/2fa/totp-verify.ts","../src/routes/2fa/totp-disable.ts","../src/routes/2fa/email-setup.ts","../src/routes/2fa/email-verify.ts","../src/routes/2fa/email-send-code.ts","../src/routes/2fa/email-disable.ts","../src/routes/2fa/trusted-devices.ts","../src/routes/2fa/backup-codes.ts","../src/routes/2fa/challenge.ts","../src/routes/2fa/challenge-resend.ts","../src/lib/email/webhook-verifier.ts"],"sourcesContent":["// Password hashing and validation utilities\n// Following OWASP 2024 best practices + bcrypt pepper hardening\n\nimport bcrypt from 'bcryptjs';\nimport logger, { logError } from '../lib/logger';\n\nconst SALT_ROUNDS = 11; // Bcrypt cost factor (11 rounds = ~75-150ms on Unbound)\n\n/**\n * HMAC-SHA256 prehash with pepper (fixes bcrypt 72-byte limit + adds pepper hardening)\n * @param password - Plain text password (after NFKC normalization)\n * @param pepper - Secret pepper key from environment\n * @returns Base64-encoded HMAC-SHA256 hash\n */\nasync function prehashWithPepper(password: string, pepper: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst key = await crypto.subtle.importKey(\n\t\t'raw',\n\t\tencoder.encode(pepper),\n\t\t{ name: 'HMAC', hash: 'SHA-256' },\n\t\tfalse,\n\t\t['sign']\n\t);\n\n\tconst signature = await crypto.subtle.sign('HMAC', key, encoder.encode(password));\n\n\t// Convert to base64 (bcrypt-safe format)\n\treturn btoa(String.fromCharCode(...new Uint8Array(signature)));\n}\n\n/**\n * Hash a password using pepper-hardened bcrypt\n * Flow: Normalize (NFKC) → HMAC-SHA256 (pepper) → Base64 → bcrypt\n * @param password - Plain text password\n * @param pepper - Secret pepper key (from env.PASSWORD_PEPPER_V1)\n * @returns Hashed password\n */\nexport async function hashPassword(password: string, pepper: string): Promise<string> {\n\t// Step 1: Normalize password (NFKC) - handles Unicode edge cases\n\tconst normalized = password.normalize('NFKC');\n\n\t// Step 2: HMAC-SHA256 with pepper → Base64 (fixes 72-byte limit + adds pepper)\n\tconst prehashed = await prehashWithPepper(normalized, pepper);\n\n\t// Step 3: Bcrypt the prehash\n\treturn await bcrypt.hash(prehashed, SALT_ROUNDS);\n}\n\n/**\n * Verify a password against its hash using pepper-hardened bcrypt\n * @param password - Plain text password to verify\n * @param hash - Stored bcrypt hash\n * @param pepper - Secret pepper key (from env.PASSWORD_PEPPER_V1 or V2)\n * @returns True if password matches\n */\nexport async function verifyPassword(\n\tpassword: string,\n\thash: string,\n\tpepper: string\n): Promise<boolean> {\n\ttry {\n\t\t// Step 1: Normalize password\n\t\tconst normalized = password.normalize('NFKC');\n\n\t\t// Step 2: HMAC-SHA256 with pepper → Base64\n\t\tconst prehashed = await prehashWithPepper(normalized, pepper);\n\n\t\t// Step 3: Compare with bcrypt (constant-time)\n\t\treturn await bcrypt.compare(prehashed, hash);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Verify password with pepper rotation support\n * Tries current pepper first, then falls back to previous pepper if provided\n * @param password - Plain text password to verify\n * @param hash - Stored bcrypt hash\n * @param currentPepper - Current pepper (v1)\n * @param previousPepper - Previous pepper (v2) for rotation support (optional)\n * @returns Object with verification result and which pepper succeeded\n */\nexport async function verifyPasswordWithRotation(\n\tpassword: string,\n\thash: string,\n\tcurrentPepper: string,\n\tpreviousPepper?: string\n): Promise<{ verified: boolean; usedPreviousPepper: boolean }> {\n\t// Try current pepper first (v1)\n\tconst currentMatch = await verifyPassword(password, hash, currentPepper);\n\tif (currentMatch) {\n\t\treturn { verified: true, usedPreviousPepper: false };\n\t}\n\n\t// If current fails and previous pepper exists, try it (v2)\n\tif (previousPepper) {\n\t\tconst previousMatch = await verifyPassword(password, hash, previousPepper);\n\t\tif (previousMatch) {\n\t\t\treturn { verified: true, usedPreviousPepper: true };\n\t\t}\n\t}\n\n\t// Both failed\n\treturn { verified: false, usedPreviousPepper: false };\n}\n\n/**\n * Validate password meets security requirements\n * OWASP 2024: Focus on length over complexity\n * @param password - Password to validate\n * @returns Validation result\n */\nexport function validatePassword(password: string): {\n\tvalid: boolean;\n\terror?: string;\n} {\n\tif (password.length < 12) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: 'Password must be at least 12 characters',\n\t\t};\n\t}\n\n\tif (password.length > 128) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: 'Password is too long (max 128 characters)',\n\t\t};\n\t}\n\n\treturn { valid: true };\n}\n\n/**\n * Check if password has been compromised using Have I Been Pwned API\n * Uses k-anonymity model - only first 5 chars of SHA-1 hash are sent\n * @param password - Password to check\n * @returns True if password found in breach database\n */\nexport async function checkBreachedPassword(password: string): Promise<boolean> {\n\ttry {\n\t\t// Hash password with SHA-1\n\t\tconst encoder = new TextEncoder();\n\t\tconst data = encoder.encode(password);\n\t\tconst hashBuffer = await crypto.subtle.digest('SHA-1', data);\n\n\t\t// Convert to hex string\n\t\tconst hashArray = Array.from(new Uint8Array(hashBuffer));\n\t\tconst hashHex = hashArray\n\t\t\t.map((b) => b.toString(16).padStart(2, '0'))\n\t\t\t.join('')\n\t\t\t.toUpperCase();\n\n\t\t// Use k-anonymity: only send first 5 characters\n\t\tconst prefix = hashHex.slice(0, 5);\n\t\tconst suffix = hashHex.slice(5);\n\n\t\t// Query HIBP API\n\t\tconst response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`, {\n\t\t\theaders: {\n\t\t\t\t'User-Agent': 'API-Client',\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\t// If API fails, fail closed (assume breached) for security\n\t\t\tlogger.warn('HIBP API check failed - failing closed', { statusText: response.statusText });\n\t\t\treturn true;\n\t\t}\n\n\t\tconst text = await response.text();\n\n\t\t// Check if our suffix appears in the response\n\t\tconst lines = text.split('\\n');\n\t\tfor (const line of lines) {\n\t\t\tconst [hashSuffix] = line.split(':');\n\t\t\tif (hashSuffix === suffix) {\n\t\t\t\treturn true; // Password found in breach database\n\t\t\t}\n\t\t}\n\n\t\treturn false; // Password not found in breaches\n\t} catch (error) {\n\t\t// If check fails, fail closed (assume breached) for security\n\t\tlogError(error, { context: 'hibp_breach_check' });\n\t\treturn true;\n\t}\n}\n\n/**\n * Validate password with optional breach check\n * @param password - Password to validate\n * @param checkBreaches - Whether to check against HIBP database (default: true)\n * @returns Validation result with optional breach warning\n */\nexport async function validatePasswordWithBreachCheck(\n\tpassword: string,\n\tcheckBreaches: boolean = true\n): Promise<{\n\tvalid: boolean;\n\terror?: string;\n\twarning?: string;\n}> {\n\t// Basic validation\n\tconst basicValidation = validatePassword(password);\n\tif (!basicValidation.valid) {\n\t\treturn basicValidation;\n\t}\n\n\t// Optional breach check\n\tif (checkBreaches) {\n\t\tconst isBreached = await checkBreachedPassword(password);\n\t\tif (isBreached) {\n\t\t\treturn {\n\t\t\t\tvalid: false,\n\t\t\t\terror:\n\t\t\t\t\t'For your protection, our system flagged this password as weak. Please choose a stronger one to keep your account safe.',\n\t\t\t};\n\t\t}\n\t}\n\n\treturn { valid: true };\n}\n","/**\n * Lightweight structured logger for Cloudflare Workers\n *\n * Passes objects directly to console methods for proper field indexing.\n * Workers Logs automatically extracts and indexes object properties.\n *\n * @see https://developers.cloudflare.com/workers/observability/logs/\n */\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\ninterface LogMetadata {\n\t[key: string]: unknown;\n}\n\n/**\n * Base logger that outputs structured JSON\n */\nclass Logger {\n\tprivate minLevel: LogLevel;\n\tprivate isSilent: boolean;\n\n\tconstructor(minLevel: LogLevel = 'info') {\n\t\tthis.minLevel = minLevel;\n\t\t// Silent mode for tests\n\t\t// Check for vitest global in Cloudflare Workers test pool\n\t\tthis.isSilent = typeof globalThis !== 'undefined' && 'vitest' in globalThis;\n\t}\n\n\tprivate shouldLog(level: LogLevel): boolean {\n\t\tconst levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n\t\treturn levels.indexOf(level) >= levels.indexOf(this.minLevel);\n\t}\n\n\tprivate log(level: LogLevel, message: string, meta?: LogMetadata): void {\n\t\tif (!this.shouldLog(level)) return;\n\n\t\t// Silent mode - skip all output during tests\n\t\tif (this.isSilent) return;\n\n\t\tconst logEntry = {\n\t\t\tlevel,\n\t\t\tmessage,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t...meta,\n\t\t};\n\n\t\t// Pass object directly for proper field indexing\n\t\tswitch (level) {\n\t\t\tcase 'error':\n\t\t\t\tconsole.error(logEntry);\n\t\t\t\tbreak;\n\t\t\tcase 'warn':\n\t\t\t\tconsole.warn(logEntry);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tconsole.log(logEntry);\n\t\t}\n\t}\n\n\tdebug(message: string, meta?: LogMetadata): void {\n\t\tthis.log('debug', message, meta);\n\t}\n\n\tinfo(message: string, meta?: LogMetadata): void {\n\t\tthis.log('info', message, meta);\n\t}\n\n\twarn(message: string, meta?: LogMetadata): void {\n\t\tthis.log('warn', message, meta);\n\t}\n\n\terror(message: string, meta?: LogMetadata): void {\n\t\tthis.log('error', message, meta);\n\t}\n\n\t/**\n\t * Set minimum log level\n\t */\n\tsetLevel(level: LogLevel): void {\n\t\tthis.minLevel = level;\n\t}\n\n\t/**\n\t * Enable/disable silent mode\n\t * Silent mode suppresses all log output (useful for tests)\n\t */\n\tsetSilent(silent: boolean): void {\n\t\tthis.isSilent = silent;\n\t}\n}\n\n// Singleton instance\nconst logger = new Logger();\n\n// Convenience functions for common logging patterns\n\n/**\n * Log HTTP request\n */\nexport function logRequest(req: Request, metadata?: LogMetadata): void {\n\tlogger.info('HTTP request', {\n\t\ttype: 'request',\n\t\tmethod: req.method,\n\t\turl: req.url,\n\t\t...metadata,\n\t});\n}\n\n/**\n * Serialize error object for comprehensive logging\n * Captures message, stack, cause, and all custom properties\n */\nexport function serializeError(error: unknown): Record<string, unknown> {\n\tif (error instanceof Error) {\n\t\tconst serialized: Record<string, unknown> = {\n\t\t\tname: error.name,\n\t\t\tmessage: error.message || '(no message)',\n\t\t\tstack: error.stack,\n\t\t\tcause: error.cause,\n\t\t\ttoString: error.toString(),\n\t\t};\n\n\t\t// Capture all enumerable properties (like .retryable, .overloaded from Cloudflare)\n\t\ttry {\n\t\t\tObject.getOwnPropertyNames(error).forEach((key) => {\n\t\t\t\tif (!serialized[key]) {\n\t\t\t\t\t// Don't override above\n\t\t\t\t\tconst value = (error as unknown as Record<string, unknown>)[key];\n\t\t\t\t\t// Avoid circular references and functions\n\t\t\t\t\tif (typeof value !== 'function' && key !== 'stack') {\n\t\t\t\t\t\tserialized[key] = value;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore errors accessing properties\n\t\t}\n\n\t\treturn serialized;\n\t}\n\n\tif (typeof error === 'object' && error !== null) {\n\t\ttry {\n\t\t\treturn {\n\t\t\t\ttype: 'object',\n\t\t\t\tvalue: String(error),\n\t\t\t\tjson: JSON.stringify(error),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn {\n\t\t\t\ttype: 'object',\n\t\t\t\tvalue: String(error),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: typeof error,\n\t\tvalue: String(error),\n\t};\n}\n\n/**\n * Log error with full context\n * Fields are automatically indexed in Workers Logs\n */\nexport function logError(error: Error | unknown, context?: LogMetadata): void {\n\t// Log to console for Workers Logs (indexed and searchable)\n\tconsole.error({\n\t\tlevel: 'error',\n\t\tmessage: error instanceof Error ? error.message || 'Error (no message)' : 'Unknown error',\n\t\terror: serializeError(error),\n\t\t...context,\n\t\ttimestamp: new Date().toISOString(),\n\t});\n}\n\n/**\n * Log workflow error with enhanced observability\n *\n * Creates structured logs optimized for Cloudflare Workers Logs filtering.\n * Key features:\n * - Clear \"WORKFLOW_ERROR:\" prefix in message for easy search\n * - `workflowError: true` field for filtering: `workflowError:true`\n * - `instanceId` for correlation with Cloudflare's infrastructure logs\n * - Full error serialization with stack traces\n *\n * Query examples in Cloudflare Observability:\n * - Filter: `workflowError:true`\n * - Search: `WORKFLOW_ERROR:`\n * - Correlate: Use `instanceId` to match with `$workers.requestId`\n *\n * @see https://developers.cloudflare.com/workers/observability/logs/workers-logs/\n */\nexport function logWorkflowError(\n\terror: Error | unknown,\n\tcontext: LogMetadata & { workflow: string; instanceId: string }\n): void {\n\tconst errorMessage = error instanceof Error ? error.message || '(no message)' : 'Unknown error';\n\tconst errorName = error instanceof Error ? error.name : typeof error;\n\tconst { workflow, instanceId, ...restContext } = context;\n\n\t// Log with clear, searchable message pattern\n\t// The \"WORKFLOW_ERROR:\" prefix makes it easy to find in observability\n\tconsole.error({\n\t\tlevel: 'error',\n\t\tmessage: `WORKFLOW_ERROR: ${workflow} - ${errorMessage}`,\n\t\t// Key fields for filtering in Cloudflare Observability\n\t\tworkflowError: true,\n\t\tworkflow,\n\t\tinstanceId,\n\t\terrorName,\n\t\terrorMessage,\n\t\t// Full error details\n\t\terror: serializeError(error),\n\t\t// All additional context (excluding workflow/instanceId to avoid duplication)\n\t\t...restContext,\n\t\ttimestamp: new Date().toISOString(),\n\t});\n}\n\n/**\n * Log workflow success. Filter in observability with `workflowSuccess:true`\n */\nexport function logWorkflowSuccess(\n\tworkflow: string,\n\tinstanceId: string,\n\tcontext?: LogMetadata\n): void {\n\tconsole.log({\n\t\tlevel: 'info',\n\t\tmessage: `WORKFLOW_SUCCESS: ${workflow}`,\n\t\tworkflowSuccess: true,\n\t\tworkflow,\n\t\tinstanceId,\n\t\t...context,\n\t\ttimestamp: new Date().toISOString(),\n\t});\n}\n\n/**\n * Log security event\n */\nexport function logSecurityEvent(\n\tevent: string,\n\tseverity: 'low' | 'medium' | 'high' | 'critical',\n\tmetadata?: LogMetadata\n): void {\n\tlogger.warn(`Security event: ${event}`, {\n\t\ttype: 'security',\n\t\tevent,\n\t\tseverity,\n\t\t...metadata,\n\t});\n}\n\n/**\n * Log performance metric\n */\nexport function logPerformance(\n\toperation: string,\n\tdurationMs: number,\n\tmetadata?: LogMetadata\n): void {\n\tlogger.info(`Performance: ${operation}`, {\n\t\ttype: 'performance',\n\t\toperation,\n\t\tdurationMs,\n\t\t...metadata,\n\t});\n}\n\n/**\n * Initialize logger with environment-based log level\n */\nexport function initLogger(env: Record<string, unknown> & { LOG_LEVEL?: string }): void {\n\tconst level = (env.LOG_LEVEL as LogLevel) || 'info';\n\tlogger.setLevel(level);\n\tlogger.info('Logger initialized', { level });\n}\n\nexport default logger;\n","// Session management utilities\n// Uses database-stored sessions with HttpOnly cookies\n// Fully migrated to Drizzle ORM\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\nimport { eq, and, gt, sql } from 'drizzle-orm';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\nimport { generateSecureToken } from './tokens';\nimport logger from '../lib/logger';\nimport { AUTH_DEFAULTS } from './config';\n\n// Table type definitions for dependency injection\ntype SessionsTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\tuserId: any;\n\t\texpiresAt: any;\n\t\tcreatedAt: any;\n\t\tlastActiveAt: any;\n\t\tfingerprint: any;\n\t\tipAddress: any;\n\t};\n\tdialect: 'pg';\n}>;\n\ntype UsersTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\temail: any;\n\t\temailVerified: any;\n\t\tsessionVersion: any;\n\t\tcreatedAt: any;\n\t\tupdatedAt: any;\n\t};\n\tdialect: 'pg';\n}>;\n\nexport interface SessionTables {\n\tsessions: SessionsTable;\n\tusers: UsersTable;\n}\n\nexport interface SessionData {\n\tuser: {\n\t\tid: string;\n\t\temail: string;\n\t\temailVerified: boolean;\n\t\tsessionVersion: number;\n\t\tcreatedAt: Date;\n\t\tupdatedAt: Date;\n\t};\n\tsession: {\n\t\tid: string;\n\t\tuserId: string;\n\t\texpiresAt: Date;\n\t\tcreatedAt: Date;\n\t\tlastActiveAt: Date | null;\n\t};\n}\n\n/**\n * Create a new session for a user\n * @param db - Drizzle database instance\n * @param tables - Session tables (sessions)\n * @param userId - User ID\n * @param fingerprint - Browser fingerprint (optional)\n * @param ipAddress - Client IP address (optional)\n * @param sessionTtlMs - Session TTL in milliseconds (optional)\n * @returns Session ID\n */\nexport async function createSession(\n\tdb: DrizzleDB,\n\ttables: Pick<SessionTables, 'sessions'>,\n\tuserId: string,\n\tfingerprint?: string,\n\tipAddress?: string,\n\tsessionTtlMs: number = AUTH_DEFAULTS.SESSION_TTL_DAYS * 24 * 60 * 60 * 1000\n): Promise<string> {\n\tconst { sessions } = tables;\n\tconst sessionId = generateSecureToken(32); // 64 hex characters\n\tconst expiresAt = Date.now() + sessionTtlMs;\n\tconst now = Date.now();\n\n\tlogger.debug('Creating session', {\n\t\tsessionId: sessionId.slice(0, 8),\n\t\tuserId,\n\t\tfingerprint: fingerprint,\n\t\thasFingerprint: !!fingerprint,\n\t\tipAddress,\n\t});\n\n\tawait db.insert(sessions).values({\n\t\tid: sessionId,\n\t\tuserId,\n\t\texpiresAt,\n\t\tcreatedAt: now,\n\t\tlastActiveAt: now,\n\t\tfingerprint: fingerprint || null,\n\t\tipAddress: ipAddress || null,\n\t});\n\n\treturn sessionId;\n}\n\n/**\n * Validate a session and return user data\n * @param db - Drizzle database instance\n * @param tables - Session tables (sessions, users)\n * @param sessionId - Session ID from cookie\n * @param currentFingerprint - Current browser fingerprint (optional)\n * @returns Session data or null if invalid\n */\nexport async function validateSession(\n\tdb: DrizzleDB,\n\ttables: SessionTables,\n\tsessionId: string,\n\tcurrentFingerprint?: string\n): Promise<SessionData | null> {\n\tconst { sessions, users } = tables;\n\n\tlogger.debug('Validating session', {\n\t\tsessionId: sessionId?.slice(0, 8),\n\t\tcurrentFingerprint: currentFingerprint,\n\t\thasFingerprintCheck: !!currentFingerprint,\n\t});\n\n\tif (!sessionId || sessionId.length !== 64) {\n\t\treturn null;\n\t}\n\n\t// Query session with user data using Drizzle join\n\tconst result = await db\n\t\t.select({\n\t\t\tsessionId: sessions.id,\n\t\t\tsessionExpiresAt: sessions.expiresAt,\n\t\t\tsessionCreatedAt: sessions.createdAt,\n\t\t\tsessionLastActiveAt: sessions.lastActiveAt,\n\t\t\tsessionFingerprint: sessions.fingerprint,\n\t\t\tuserId: users.id,\n\t\t\tuserEmail: users.email,\n\t\t\tuserEmailVerified: users.emailVerified,\n\t\t\tuserSessionVersion: users.sessionVersion,\n\t\t\tuserCreatedAt: users.createdAt,\n\t\t\tuserUpdatedAt: users.updatedAt,\n\t\t})\n\t\t.from(sessions)\n\t\t.innerJoin(users, eq(sessions.userId, users.id))\n\t\t.where(and(eq(sessions.id, sessionId), gt(sessions.expiresAt, Date.now())))\n\t\t.limit(1);\n\n\tif (result.length === 0) {\n\t\treturn null;\n\t}\n\n\tconst row = result[0];\n\n\t// Fingerprint validation: Client requests only (SSR has different User-Agent)\n\t// Mismatch is logged but accepted - compensating controls: CSRF, IP, expiration\n\tif (\n\t\tcurrentFingerprint &&\n\t\trow.sessionFingerprint &&\n\t\trow.sessionFingerprint !== currentFingerprint\n\t) {\n\t\t// Fingerprint mismatch detected - could be SSR or hijacking attempt\n\t\t// Log for investigation but don't automatically invalidate (SSR causes false positives)\n\t\tlogger.info('Session fingerprint mismatch - likely SSR request', {\n\t\t\ttype: 'security',\n\t\t\tevent: 'fingerprint_mismatch_ssr',\n\t\t\tseverity: 'info',\n\t\t\tsessionId: sessionId.slice(0, 8),\n\t\t\tuserId: row.userId,\n\t\t\tnote: 'SSR requests have different User-Agent, causing expected mismatch',\n\t\t});\n\t\t// Accept mismatch - SSR is a valid use case\n\t\t// Other controls (CSRF, IP, expiration) provide security\n\t}\n\n\t// Refresh session expiration (sliding window)\n\tawait refreshSession(db, { sessions }, sessionId);\n\n\treturn {\n\t\tuser: {\n\t\t\tid: row.userId,\n\t\t\temail: row.userEmail,\n\t\t\temailVerified: Boolean(row.userEmailVerified),\n\t\t\tsessionVersion: row.userSessionVersion || 1,\n\t\t\tcreatedAt: row.userCreatedAt || new Date(),\n\t\t\tupdatedAt: row.userUpdatedAt || new Date(),\n\t\t},\n\t\tsession: {\n\t\t\tid: sessionId,\n\t\t\tuserId: row.userId,\n\t\t\texpiresAt: new Date(row.sessionExpiresAt),\n\t\t\tcreatedAt: new Date(row.sessionCreatedAt),\n\t\t\tlastActiveAt: row.sessionLastActiveAt ? new Date(row.sessionLastActiveAt) : null,\n\t\t},\n\t};\n}\n\n/**\n * Refresh session expiration (sliding window)\n * @param db - Drizzle database instance\n * @param tables - Session tables (sessions)\n * @param sessionId - Session ID\n * @param sessionTtlMs - Session TTL in milliseconds (optional)\n */\nexport async function refreshSession(\n\tdb: DrizzleDB,\n\ttables: Pick<SessionTables, 'sessions'>,\n\tsessionId: string,\n\tsessionTtlMs: number = AUTH_DEFAULTS.SESSION_TTL_DAYS * 24 * 60 * 60 * 1000\n): Promise<void> {\n\tconst { sessions } = tables;\n\tconst newExpiration = Date.now() + sessionTtlMs;\n\n\tawait db\n\t\t.update(sessions)\n\t\t.set({\n\t\t\texpiresAt: newExpiration,\n\t\t\tlastActiveAt: Date.now(),\n\t\t})\n\t\t.where(eq(sessions.id, sessionId));\n}\n\n/**\n * Delete a session (logout)\n * @param db - Drizzle database instance\n * @param tables - Session tables (sessions)\n * @param sessionId - Session ID\n */\nexport async function deleteSession(\n\tdb: DrizzleDB,\n\ttables: Pick<SessionTables, 'sessions'>,\n\tsessionId: string\n): Promise<void> {\n\tconst { sessions } = tables;\n\tawait db.delete(sessions).where(eq(sessions.id, sessionId));\n}\n\n/**\n * Delete all sessions for a user (logout all devices)\n * @param db - Drizzle database instance\n * @param tables - Session tables (sessions)\n * @param userId - User ID\n */\nexport async function deleteAllUserSessions(\n\tdb: DrizzleDB,\n\ttables: Pick<SessionTables, 'sessions'>,\n\tuserId: string\n): Promise<void> {\n\tconst { sessions } = tables;\n\tawait db.delete(sessions).where(eq(sessions.userId, userId));\n}\n\n/**\n * Invalidate all sessions after password change\n * @param db - Drizzle database instance\n * @param tables - Session tables (sessions, users)\n * @param userId - User ID\n */\nexport async function invalidateAllUserSessions(\n\tdb: DrizzleDB,\n\ttables: SessionTables,\n\tuserId: string\n): Promise<void> {\n\tconst { sessions, users } = tables;\n\t// Increment session version to invalidate all existing sessions\n\tawait db\n\t\t.update(users)\n\t\t.set({\n\t\t\tsessionVersion: sql`COALESCE(${users.sessionVersion}, 0) + 1`,\n\t\t})\n\t\t.where(eq(users.id, userId));\n\n\t// Also delete session records\n\tawait deleteAllUserSessions(db, { sessions }, userId);\n}\n","// Token generation and hashing utilities\n// All tokens are hashed before storage for security\n\n/**\n * Generate a cryptographically secure random token\n * @param bytes - Number of bytes (default: 64 = 128 hex chars)\n * @returns Hex-encoded token\n */\nexport function generateSecureToken(bytes: number = 64): string {\n\tconst buffer = new Uint8Array(bytes);\n\tcrypto.getRandomValues(buffer);\n\treturn Array.from(buffer, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Hash a token using SHA-256 for storage\n * NEVER store tokens in plain text - always hash first\n * @param token - Token to hash\n * @returns SHA-256 hash as hex string\n */\nexport async function hashToken(token: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(token);\n\tconst hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\tconst hashArray = Array.from(new Uint8Array(hashBuffer));\n\treturn hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n}\n","/**\n * Centralized auth configuration\n * All hardcoded auth values should be defined here with env var overrides\n */\n\nexport const AUTH_DEFAULTS = {\n\tSESSION_TTL_DAYS: 7,\n\tLOCKOUT_DURATION_MINUTES: 30,\n\tLOCKOUT_MAX_ATTEMPTS: 5,\n\tPASSWORD_RESET_TTL_MINUTES: 15,\n\tTWO_FACTOR_CHALLENGE_TTL_SECONDS: 300, // 5 minutes\n\tAPP_NAME: 'App',\n} as const;\n","import { setCookie, deleteCookie } from 'hono/cookie';\nimport type { Context } from 'hono';\nimport { TRUSTED_DEVICE_TTL_MS, TRUSTED_DEVICE_COOKIE_BASE } from './2fa';\nimport type { Env } from '../types';\nimport { AUTH_DEFAULTS } from './config';\n\n// Environment-specific cookie names to prevent cross-environment session conflicts\nconst COOKIE_NAMES: Record<string, string> = {\n\tproduction: 'sessionId',\n\tstaging: 'staging_sessionId',\n\tdevelopment: 'sessionId',\n};\n\nexport function getSessionCookieName(env?: { ENVIRONMENT?: string }): string {\n\tconst environment = env?.ENVIRONMENT || 'development';\n\treturn COOKIE_NAMES[environment] || COOKIE_NAMES.development;\n}\n\nfunction isProduction(env?: { ENVIRONMENT?: string }): boolean {\n\tconst environment = env?.ENVIRONMENT;\n\treturn environment === 'production' || environment === 'staging';\n}\n\n/**\n * Get cookie security attributes based on environment\n * Production/Staging: SameSite=None + Secure (required for cross-origin deployments)\n * Development: SameSite=Lax (same-origin localhost)\n */\nfunction getCookieSecurityFlags(env?: { ENVIRONMENT?: string; COOKIE_DOMAIN?: string }): string {\n\tif (isProduction(env)) {\n\t\tconst domain = env?.COOKIE_DOMAIN ? `; Domain=${env.COOKIE_DOMAIN}` : '';\n\t\treturn `HttpOnly; Secure; SameSite=None${domain}`;\n\t}\n\treturn 'HttpOnly; SameSite=Lax';\n}\n\nexport function setSessionCookie(sessionId: string, env?: { ENVIRONMENT?: string }): string {\n\tconst maxAge = AUTH_DEFAULTS.SESSION_TTL_DAYS * 24 * 60 * 60;\n\tconst cookieName = getSessionCookieName(env);\n\tconst securityFlags = getCookieSecurityFlags(env);\n\treturn `${cookieName}=${sessionId}; Path=/; ${securityFlags}; Max-Age=${maxAge}`;\n}\n\nexport function clearSessionCookie(env?: { ENVIRONMENT?: string }): string {\n\tconst cookieName = getSessionCookieName(env);\n\tconst securityFlags = getCookieSecurityFlags(env);\n\treturn `${cookieName}=; Path=/; ${securityFlags}; Max-Age=0`;\n}\n\n// ========================================\n// Trusted Device Cookies\n// ========================================\n\nexport function getTrustedDeviceCookieName(env: Env): string {\n\treturn env.ENVIRONMENT === 'staging' ? `staging_${TRUSTED_DEVICE_COOKIE_BASE}` : TRUSTED_DEVICE_COOKIE_BASE;\n}\n\nexport function setTrustedDeviceCookie(c: Context, env: Env, token: string): void {\n\tconst cookieName = getTrustedDeviceCookieName(env);\n\tconst isProd = env.ENVIRONMENT !== 'development';\n\tconst maxAge = Math.floor(TRUSTED_DEVICE_TTL_MS / 1000);\n\n\tsetCookie(c, cookieName, token, {\n\t\tpath: '/',\n\t\thttpOnly: true,\n\t\tsecure: isProd,\n\t\tsameSite: isProd ? 'None' : 'Lax',\n\t\tmaxAge,\n\t\t...(env.COOKIE_DOMAIN && { domain: env.COOKIE_DOMAIN }),\n\t});\n}\n\nexport function clearTrustedDeviceCookie(c: Context, env: Env): void {\n\tdeleteCookie(c, getTrustedDeviceCookieName(env), { path: '/' });\n}\n\n// ========================================\n// 2FA Challenge Cookies\n// ========================================\n\nconst CHALLENGE_COOKIE_BASE = '2fa_challenge';\nconst CHALLENGE_TTL_SECONDS = AUTH_DEFAULTS.TWO_FACTOR_CHALLENGE_TTL_SECONDS;\n\nexport function getChallengeCookieName(env: Env): string {\n\treturn env.ENVIRONMENT === 'staging' ? `staging_${CHALLENGE_COOKIE_BASE}` : CHALLENGE_COOKIE_BASE;\n}\n\nexport function setChallengeCookie(c: Context, env: Env, token: string): void {\n\tconst cookieName = getChallengeCookieName(env);\n\tconst isProduction = env.ENVIRONMENT !== 'development';\n\tconst domain = env.COOKIE_DOMAIN || undefined;\n\n\tsetCookie(c, cookieName, token, {\n\t\tpath: '/',\n\t\thttpOnly: true,\n\t\tsecure: isProduction,\n\t\tsameSite: isProduction ? 'None' : 'Lax',\n\t\tmaxAge: CHALLENGE_TTL_SECONDS,\n\t\t...(domain && { domain }),\n\t});\n}\n\nexport function clearChallengeCookie(c: Context, env: Env): void {\n\tconst domain = env.COOKIE_DOMAIN || undefined;\n\tdeleteCookie(c, getChallengeCookieName(env), { path: '/', ...(domain && { domain }) });\n}\n","/**\n * TOTP (Time-based One-Time Password) utilities\n * RFC 6238 compliant with replay prevention\n */\n\nimport { authenticator } from 'otplib';\nimport { AUTH_DEFAULTS } from '../config';\n\n// Configure authenticator with 1 time step tolerance (90-second window)\nauthenticator.options = { window: 1 };\n\n/** TOTP period in seconds */\nconst TOTP_PERIOD = 30;\n\n/**\n * Get current TOTP counter (time step)\n */\nexport function getTotpCounter(): number {\n\treturn Math.floor(Date.now() / 1000 / TOTP_PERIOD);\n}\n\n/**\n * Generate a cryptographically secure TOTP secret\n * @returns Base32-encoded 20-byte secret (32 characters)\n */\nexport function generateTotpSecret(): string {\n\treturn authenticator.generateSecret(20);\n}\n\n/**\n * Generate the otpauth:// URI for QR code scanning\n * @param secret - Base32-encoded TOTP secret\n * @param email - User's email address\n * @param issuer - Application name shown in authenticator apps (default: AUTH_DEFAULTS.APP_NAME)\n */\nexport function generateQrCodeUri(\n\tsecret: string,\n\temail: string,\n\tissuer: string = AUTH_DEFAULTS.APP_NAME\n): string {\n\tconst encodedEmail = encodeURIComponent(email);\n\tconst encodedIssuer = encodeURIComponent(issuer);\n\treturn `otpauth://totp/${encodedIssuer}:${encodedEmail}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;\n}\n\nexport type TotpVerifyResult =\n\t| { valid: true; counter: number }\n\t| { valid: false; counter?: undefined };\n\n/**\n * Verify a TOTP code with replay prevention\n * @param secret - Base32-encoded TOTP secret\n * @param code - 6-digit TOTP code from user\n * @param lastCounter - Last accepted counter (null if first verification)\n * @returns Verification result with counter if valid\n */\nexport function verifyTotpCode(\n\tsecret: string,\n\tcode: string,\n\tlastCounter: number | null\n): TotpVerifyResult {\n\tif (!code || code.length !== 6) {\n\t\treturn { valid: false };\n\t}\n\n\ttry {\n\t\tconst delta = authenticator.checkDelta(code, secret);\n\t\tif (delta === null) {\n\t\t\treturn { valid: false };\n\t\t}\n\n\t\tconst actualTokenCounter = getTotpCounter() + delta;\n\n\t\tif (lastCounter !== null && actualTokenCounter <= lastCounter) {\n\t\t\treturn { valid: false };\n\t\t}\n\n\t\treturn { valid: true, counter: actualTokenCounter };\n\t} catch {\n\t\treturn { valid: false };\n\t}\n}\n\n/**\n * Encrypt a TOTP secret using AES-256-GCM\n */\nexport async function encryptTotpSecret(secret: string, keyHex: string): Promise<string> {\n\tconst keyBytes = hexToBytes(keyHex);\n\tconst key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, [\n\t\t'encrypt',\n\t]);\n\n\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\tconst plaintext = new TextEncoder().encode(secret);\n\n\tconst ciphertextWithTag = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext);\n\n\tconst ciphertext = new Uint8Array(ciphertextWithTag.slice(0, -16));\n\tconst authTag = new Uint8Array(ciphertextWithTag.slice(-16));\n\n\treturn `${bytesToBase64(iv)}:${bytesToBase64(authTag)}:${bytesToBase64(ciphertext)}`;\n}\n\n/**\n * Decrypt a TOTP secret using AES-256-GCM\n */\nexport async function decryptTotpSecret(encrypted: string, keyHex: string): Promise<string> {\n\tconst [ivB64, authTagB64, ciphertextB64] = encrypted.split(':');\n\tif (!ivB64 || !authTagB64 || !ciphertextB64) {\n\t\tthrow new Error('Invalid encrypted format');\n\t}\n\n\tconst keyBytes = hexToBytes(keyHex);\n\tconst key = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, [\n\t\t'decrypt',\n\t]);\n\n\tconst iv = base64ToBytes(ivB64);\n\tconst authTag = base64ToBytes(authTagB64);\n\tconst ciphertext = base64ToBytes(ciphertextB64);\n\n\tconst ciphertextWithTag = new Uint8Array(ciphertext.length + authTag.length);\n\tciphertextWithTag.set(ciphertext);\n\tciphertextWithTag.set(authTag, ciphertext.length);\n\n\tconst plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertextWithTag);\n\treturn new TextDecoder().decode(plaintext);\n}\n\n// Encoding helpers\nfunction hexToBytes(hex: string): Uint8Array {\n\treturn new Uint8Array(hex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)));\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n\treturn btoa(String.fromCharCode(...bytes));\n}\n\nfunction base64ToBytes(base64: string): Uint8Array {\n\tconst binary = atob(base64);\n\treturn new Uint8Array([...binary].map((c) => c.charCodeAt(0)));\n}\n","/**\n * Backup codes for 2FA recovery\n * Uses bcrypt for NIST SP 800-63B compliance (secrets < 112 bits)\n */\n\nimport bcrypt from 'bcryptjs';\n\n// Character set without ambiguous characters (0/O, 1/I/L)\nconst CHARSET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\n\n// Bcrypt cost factor (same as passwords for consistency)\nconst BCRYPT_ROUNDS = 11;\n\n/**\n * Generate backup codes\n * @param count - Number of codes to generate (default: 10)\n * @returns Array of 8-character backup codes\n */\nexport function generateBackupCodes(count: number = 10): string[] {\n\tconst codes: string[] = [];\n\tconst usedCodes = new Set<string>();\n\n\twhile (codes.length < count) {\n\t\tconst code = generateSingleCode();\n\t\tif (!usedCodes.has(code)) {\n\t\t\tusedCodes.add(code);\n\t\t\tcodes.push(code);\n\t\t}\n\t}\n\n\treturn codes;\n}\n\nfunction generateSingleCode(): string {\n\tconst bytes = crypto.getRandomValues(new Uint8Array(8));\n\tlet code = '';\n\tfor (let i = 0; i < 8; i++) {\n\t\tcode += CHARSET[bytes[i] % CHARSET.length];\n\t}\n\treturn code;\n}\n\n/**\n * Format a backup code for display (XXXX-XXXX)\n */\nexport function formatBackupCode(code: string): string {\n\treturn `${code.slice(0, 4)}-${code.slice(4)}`;\n}\n\n/**\n * Normalize user input for comparison\n */\nexport function normalizeBackupCode(input: string): string {\n\treturn input.toUpperCase().replace(/[-\\s]/g, '');\n}\n\n/**\n * Hash a backup code using bcrypt (NIST compliant)\n */\nexport async function hashBackupCode(code: string): Promise<string> {\n\tconst normalized = normalizeBackupCode(code);\n\treturn bcrypt.hash(normalized, BCRYPT_ROUNDS);\n}\n\n/**\n * Verify a backup code against a stored bcrypt hash\n */\nexport async function verifyBackupCode(code: string, storedHash: string): Promise<boolean> {\n\tconst normalized = normalizeBackupCode(code);\n\treturn bcrypt.compare(normalized, storedHash);\n}\n","/**\n * Trusted device management for 2FA \"Remember this device\" feature\n */\n\nimport { UAParser } from 'ua-parser-js';\nimport { generateSecureToken } from '../tokens';\n\n/** Trusted device cookie TTL: 30 days in milliseconds */\nexport const TRUSTED_DEVICE_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\n/** Trusted device cookie base name */\nexport const TRUSTED_DEVICE_COOKIE_BASE = 'trusted_device';\n\nexport type DeviceType = 'desktop' | 'mobile' | 'tablet';\n\n/**\n * Parse User-Agent to get a human-readable device name\n */\nexport function parseDeviceName(userAgent: string): string {\n\tif (!userAgent) return 'Unknown Browser';\n\n\tconst parser = new UAParser(userAgent);\n\tconst browser = parser.getBrowser().name;\n\tconst os = parser.getOS().name;\n\n\tif (!browser) return 'Unknown Browser';\n\treturn os ? `${browser} on ${os}` : browser;\n}\n\n/**\n * Parse User-Agent to determine device type (desktop, mobile, or tablet)\n */\nexport function parseDeviceType(userAgent: string): DeviceType {\n\tif (!userAgent) return 'desktop';\n\n\tconst parser = new UAParser(userAgent);\n\tconst device = parser.getDevice();\n\n\t// ua-parser-js returns 'mobile', 'tablet', 'console', 'smarttv', 'wearable', 'embedded', or undefined\n\tif (device.type === 'mobile') return 'mobile';\n\tif (device.type === 'tablet') return 'tablet';\n\n\t// Default to desktop for undefined, console, smarttv, etc.\n\treturn 'desktop';\n}\n\n/**\n * Create a new trusted device token\n */\nexport function createDeviceToken(): { token: string; expiresAt: number } {\n\treturn {\n\t\ttoken: generateSecureToken(32),\n\t\texpiresAt: Date.now() + TRUSTED_DEVICE_TTL_MS,\n\t};\n}\n\n/**\n * Validate a trusted device token for a user\n * Returns true if the token is valid and not expired\n */\nexport async function validateTrustedDevice(\n\tdb: any,\n\tuserTrustedDevicesTable: any,\n\tuserId: string,\n\ttokenHash: string\n): Promise<boolean> {\n\tconst { eq, and, gt } = await import('drizzle-orm');\n\n\tconst [device] = await db\n\t\t.select()\n\t\t.from(userTrustedDevicesTable)\n\t\t.where(\n\t\t\tand(\n\t\t\t\teq(userTrustedDevicesTable.userId, userId),\n\t\t\t\teq(userTrustedDevicesTable.tokenHash, tokenHash),\n\t\t\t\tgt(userTrustedDevicesTable.expiresAt, Date.now())\n\t\t\t)\n\t\t)\n\t\t.limit(1);\n\n\tif (device) {\n\t\t// Update last used timestamp\n\t\tawait db\n\t\t\t.update(userTrustedDevicesTable)\n\t\t\t.set({ lastUsedAt: Date.now() })\n\t\t\t.where(eq(userTrustedDevicesTable.id, device.id));\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n","/**\n * 2FA Challenge token management with KV helpers\n */\n\nimport { generateSecureToken, hashToken } from '../tokens';\n\n/** Challenge token TTL: 5 minutes */\nexport const CHALLENGE_TTL_MS = 5 * 60 * 1000;\n\n/** Max attempts before challenge is invalidated */\nexport const MAX_CHALLENGE_ATTEMPTS = 5;\n\n/** KV key prefix for challenge tokens */\nconst CHALLENGE_KV_PREFIX = '2fa_challenge:';\n\nexport interface ChallengePayload {\n\tuserId: string;\n\tmethods: ('totp' | 'email')[];\n\tinvitationToken: string | null;\n\texpiresAt: number;\n\tattempts: number;\n\tcreatedAt: number;\n}\n\nexport type ValidationResult =\n\t| { valid: true }\n\t| { valid: false; reason: 'expired' | 'max_attempts' | 'invalid' };\n\n/**\n * Create a new challenge token with full context\n */\nexport function createChallengeToken(\n\tuserId: string,\n\tmethods: ('totp' | 'email')[],\n\tinvitationToken: string | null\n): { token: string; payload: ChallengePayload } {\n\tconst now = Date.now();\n\treturn {\n\t\ttoken: generateSecureToken(32),\n\t\tpayload: {\n\t\t\tuserId,\n\t\t\tmethods,\n\t\t\tinvitationToken,\n\t\t\texpiresAt: now + CHALLENGE_TTL_MS,\n\t\t\tattempts: 0,\n\t\t\tcreatedAt: now,\n\t\t},\n\t};\n}\n\n/**\n * Validate a challenge payload\n */\nexport function validateChallengePayload(payload: ChallengePayload): ValidationResult {\n\tif (payload.expiresAt < Date.now()) {\n\t\treturn { valid: false, reason: 'expired' };\n\t}\n\tif (payload.attempts >= MAX_CHALLENGE_ATTEMPTS) {\n\t\treturn { valid: false, reason: 'max_attempts' };\n\t}\n\treturn { valid: true };\n}\n\n// KV Helper Functions\n\n/**\n * Store a challenge token in KV\n */\nexport async function storeChallengeToken(\n\tkv: KVNamespace,\n\ttoken: string,\n\tpayload: ChallengePayload\n): Promise<void> {\n\tconst tokenHash = await hashToken(token);\n\tconst kvKey = `${CHALLENGE_KV_PREFIX}${tokenHash}`;\n\tconst ttlSeconds = Math.ceil(CHALLENGE_TTL_MS / 1000);\n\tawait kv.put(kvKey, JSON.stringify(payload), { expirationTtl: ttlSeconds });\n}\n\n/**\n * Retrieve a challenge token from KV\n */\nexport async function retrieveChallengeToken(\n\tkv: KVNamespace,\n\ttoken: string\n): Promise<ChallengePayload | null> {\n\tconst tokenHash = await hashToken(token);\n\tconst kvKey = `${CHALLENGE_KV_PREFIX}${tokenHash}`;\n\tconst payloadJson = await kv.get(kvKey);\n\tif (!payloadJson) return null;\n\treturn JSON.parse(payloadJson);\n}\n\nconst KV_MIN_TTL_SECONDS = 60;\n\n/**\n * Update challenge attempts in KV\n */\nexport async function updateChallengeAttempts(\n\tkv: KVNamespace,\n\ttoken: string,\n\tpayload: ChallengePayload\n): Promise<void> {\n\tconst tokenHash = await hashToken(token);\n\tconst kvKey = `${CHALLENGE_KV_PREFIX}${tokenHash}`;\n\tconst remainingTtl = Math.ceil((payload.expiresAt - Date.now()) / 1000);\n\tif (remainingTtl > 0) {\n\t\tconst ttl = Math.max(remainingTtl, KV_MIN_TTL_SECONDS);\n\t\tawait kv.put(kvKey, JSON.stringify(payload), { expirationTtl: ttl });\n\t}\n}\n\n/**\n * Delete a challenge token from KV\n */\nexport async function deleteChallengeToken(kv: KVNamespace, token: string): Promise<void> {\n\tconst tokenHash = await hashToken(token);\n\tconst kvKey = `${CHALLENGE_KV_PREFIX}${tokenHash}`;\n\tawait kv.delete(kvKey);\n}\n","// Browser fingerprinting utilities for session hijacking detection\n// Generates fingerprint from User-Agent and Accept-Language headers\n\n/**\n * Generate a browser fingerprint from request headers\n * Uses User-Agent and Accept-Language to create a stable identifier\n * @param request - The incoming request\n * @returns SHA-256 hash (first 32 characters)\n */\nexport async function generateFingerprint(request: Request): Promise<string> {\n\tconst userAgent = request.headers.get('user-agent') || '';\n\tconst acceptLanguage = request.headers.get('accept-language') || '';\n\tconst raw = `${userAgent}|${acceptLanguage}`;\n\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(raw);\n\tconst hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\tconst hashArray = Array.from(new Uint8Array(hashBuffer));\n\tconst hash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');\n\n\treturn hash.substring(0, 32); // First 32 chars (16 bytes)\n}\n\n/**\n * Get client IP address from request headers\n * Prioritizes Cloudflare's cf-connecting-ip, falls back to x-forwarded-for\n * @param request - The incoming request\n * @returns Client IP address or 'unknown'\n */\nexport function getClientIp(request: Request): string {\n\treturn (\n\t\trequest.headers.get('cf-connecting-ip') ||\n\t\trequest.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\n\t\t'unknown'\n\t);\n}\n","// Account lockout utilities to prevent brute force attacks\n// Implements per-account lockout after failed login attempts\n\nimport { eq } from 'drizzle-orm';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\nimport logger from '../lib/logger';\nimport { AUTH_DEFAULTS } from './config';\n\n// Table type definition for dependency injection\ntype UsersTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\tlockedUntil: any;\n\t\tfailedLoginCount: any;\n\t};\n\tdialect: 'pg';\n}>;\n\nexport interface AccountLockoutTables {\n\tusers: UsersTable;\n}\n\n/**\n * Check if an account is currently locked\n * @param db - Drizzle database instance\n * @param tables - Account lockout tables (users)\n * @param userId - User ID to check\n * @returns Lock status and unlock time if locked\n */\nexport async function checkAccountLocked(\n\tdb: DrizzleDB,\n\ttables: AccountLockoutTables,\n\tuserId: string\n): Promise<{\n\tisLocked: boolean;\n\tunlockAt?: number;\n}> {\n\tconst { users } = tables;\n\n\tconst [user] = await db\n\t\t.select({ lockedUntil: users.lockedUntil })\n\t\t.from(users)\n\t\t.where(eq(users.id, userId))\n\t\t.limit(1);\n\n\tif (!user || !user.lockedUntil) {\n\t\treturn { isLocked: false };\n\t}\n\n\tconst now = Date.now();\n\tif (user.lockedUntil > now) {\n\t\treturn { isLocked: true, unlockAt: user.lockedUntil };\n\t}\n\n\t// Lock expired, clear it\n\tawait db\n\t\t.update(users)\n\t\t.set({ lockedUntil: null, failedLoginCount: 0 })\n\t\t.where(eq(users.id, userId));\n\n\treturn { isLocked: false };\n}\n\n/**\n * Increment failed login attempts for a user\n * Locks account after maxAttempts\n * @param db - Drizzle database instance\n * @param tables - Account lockout tables (users)\n * @param userId - User ID\n * @param maxAttempts - Maximum failed attempts before lockout\n * @param lockoutDurationMs - Duration of lockout in milliseconds\n */\nexport async function incrementFailedAttempts(\n\tdb: DrizzleDB,\n\ttables: AccountLockoutTables,\n\tuserId: string,\n\tmaxAttempts: number = AUTH_DEFAULTS.LOCKOUT_MAX_ATTEMPTS,\n\tlockoutDurationMs: number = AUTH_DEFAULTS.LOCKOUT_DURATION_MINUTES * 60 * 1000\n): Promise<void> {\n\tconst { users } = tables;\n\n\tconst [user] = await db\n\t\t.select({ failedLoginCount: users.failedLoginCount })\n\t\t.from(users)\n\t\t.where(eq(users.id, userId))\n\t\t.limit(1);\n\n\tconst newCount = (user?.failedLoginCount || 0) + 1;\n\n\tif (newCount >= maxAttempts) {\n\t\tconst lockUntil = Date.now() + lockoutDurationMs;\n\t\tawait db\n\t\t\t.update(users)\n\t\t\t.set({ failedLoginCount: newCount, lockedUntil: lockUntil })\n\t\t\t.where(eq(users.id, userId));\n\n\t\tlogger.warn('Account locked after failed login attempts', {\n\t\t\ttype: 'security',\n\t\t\tevent: 'account_locked',\n\t\t\tseverity: 'high',\n\t\t\tuserId: userId.slice(0, 8),\n\t\t\tattemptCount: newCount,\n\t\t\tlockUntil: new Date(lockUntil).toISOString(),\n\t\t});\n\t} else {\n\t\tawait db.update(users).set({ failedLoginCount: newCount }).where(eq(users.id, userId));\n\t}\n}\n\n/**\n * Clear account lockout and failed attempt counter\n * Called after successful login\n * @param db - Drizzle database instance\n * @param tables - Account lockout tables (users)\n * @param userId - User ID\n */\nexport async function clearAccountLockout(\n\tdb: DrizzleDB,\n\ttables: AccountLockoutTables,\n\tuserId: string\n): Promise<void> {\n\tconst { users } = tables;\n\tawait db\n\t\t.update(users)\n\t\t.set({ failedLoginCount: 0, lockedUntil: null })\n\t\t.where(eq(users.id, userId));\n}\n\n/**\n * Get minutes remaining until account unlock\n * @param unlockAt - Unlock timestamp\n * @returns Minutes remaining (rounded up)\n */\nexport function getMinutesUntilUnlock(unlockAt: number): number {\n\tconst msRemaining = unlockAt - Date.now();\n\treturn Math.max(0, Math.ceil(msRemaining / (60 * 1000)));\n}\n","/**\n * Cloudflare Turnstile test keys (always pass verification)\n * @see https://developers.cloudflare.com/turnstile/troubleshooting/testing/\n */\nconst TURNSTILE_TEST_SECRET_KEY = '1x0000000000000000000000000000000AA';\n\n/**\n * Verifies a Cloudflare Turnstile token server-side.\n * Returns true if the token is valid, false otherwise.\n *\n * In staging/test environments, uses Cloudflare's test secret key that always passes.\n * @see https://developers.cloudflare.com/turnstile/tutorials/excluding-turnstile-from-e2e-tests/\n */\nexport async function verifyTurnstileToken(\n\ttoken: string,\n\tsecretKey: string,\n\tremoteIp?: string,\n\tenvironment?: 'development' | 'staging' | 'production'\n): Promise<boolean> {\n\t// Use test secret key only for local development\n\t// Staging and production both use real verification\n\tconst effectiveSecretKey = environment === 'development' ? TURNSTILE_TEST_SECRET_KEY : secretKey;\n\n\tconst response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {\n\t\tmethod: 'POST',\n\t\theaders: { 'Content-Type': 'application/json' },\n\t\tbody: JSON.stringify({\n\t\t\tsecret: effectiveSecretKey,\n\t\t\tresponse: token,\n\t\t\tremoteip: remoteIp,\n\t\t}),\n\t});\n\tconst result = (await response.json()) as { success: boolean };\n\treturn result.success === true;\n}\n","import { eq } from 'drizzle-orm';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\nimport { verifyPassword } from './password';\nimport { generateSecureToken, hashToken } from './tokens';\n\nconst TOKEN_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours\n\n// Table type definitions for dependency injection\ntype UsersTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\temail: any;\n\t\thashedPassword: any;\n\t\tupdatedAt: any;\n\t};\n\tdialect: 'pg';\n}>;\n\ntype EmailChangeTokensTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\tuserId: any;\n\t\tnewEmail: any;\n\t\ttokenHash: any;\n\t\tcancelTokenHash: any;\n\t\texpiresAt: any;\n\t\tcreatedAt: any;\n\t};\n\tdialect: 'pg';\n}>;\n\nexport interface EmailChangeTables {\n\tusers: UsersTable;\n\temailChangeTokens: EmailChangeTokensTable;\n}\n\ninterface RequestEmailChangeParams {\n\tuserId: string;\n\tpassword: string;\n\tnewEmail: string;\n\tpepper: string;\n\tdb: DrizzleDB;\n\ttables: EmailChangeTables;\n}\n\ninterface RequestEmailChangeResult {\n\tsuccess: boolean;\n\terror?: string;\n\tconfirmToken?: string;\n\tcancelToken?: string;\n\toldEmail?: string;\n}\n\n/**\n * Request an email change - validates password and creates tokens\n */\nexport async function requestEmailChange(\n\tparams: RequestEmailChangeParams\n): Promise<RequestEmailChangeResult> {\n\tconst { userId, password, newEmail, pepper, db, tables } = params;\n\tconst { users, emailChangeTokens } = tables;\n\n\t// 1. Get user\n\tconst [user] = await db\n\t\t.select({\n\t\t\tid: users.id,\n\t\t\temail: users.email,\n\t\t\thashedPassword: users.hashedPassword,\n\t\t})\n\t\t.from(users)\n\t\t.where(eq(users.id, userId))\n\t\t.limit(1);\n\n\tif (!user || !user.hashedPassword) {\n\t\treturn { success: false, error: 'User not found' };\n\t}\n\n\t// 2. Verify password\n\tconst isValid = await verifyPassword(password, user.hashedPassword, pepper);\n\tif (!isValid) {\n\t\treturn { success: false, error: 'Incorrect password' };\n\t}\n\n\t// 3. Check new email not already in use\n\tconst [existingUser] = await db\n\t\t.select({ id: users.id })\n\t\t.from(users)\n\t\t.where(eq(users.email, newEmail.toLowerCase()))\n\t\t.limit(1);\n\n\tif (existingUser) {\n\t\treturn { success: false, error: 'Email already in use' };\n\t}\n\n\t// 4. Generate tokens\n\tconst confirmToken = generateSecureToken(32); // 64 hex chars\n\tconst cancelToken = generateSecureToken(32);\n\tconst confirmTokenHash = await hashToken(confirmToken);\n\tconst cancelTokenHash = await hashToken(cancelToken);\n\n\t// 5. Delete any existing pending change for this user\n\tawait db.delete(emailChangeTokens).where(eq(emailChangeTokens.userId, userId));\n\n\t// 6. Insert new token record\n\tawait db.insert(emailChangeTokens).values({\n\t\tuserId,\n\t\tnewEmail: newEmail.toLowerCase(),\n\t\ttokenHash: confirmTokenHash,\n\t\tcancelTokenHash: cancelTokenHash,\n\t\texpiresAt: Date.now() + TOKEN_EXPIRY_MS,\n\t\tcreatedAt: Date.now(),\n\t});\n\n\treturn {\n\t\tsuccess: true,\n\t\tconfirmToken,\n\t\tcancelToken,\n\t\toldEmail: user.email,\n\t};\n}\n\ninterface ConfirmEmailChangeParams {\n\ttoken: string;\n\tdb: DrizzleDB;\n\ttables: EmailChangeTables;\n}\n\ninterface ConfirmEmailChangeResult {\n\tsuccess: boolean;\n\terror?: string;\n}\n\n/**\n * Confirm email change - validates token and updates email\n */\nexport async function confirmEmailChange(\n\tparams: ConfirmEmailChangeParams\n): Promise<ConfirmEmailChangeResult> {\n\tconst { token, db, tables } = params;\n\tconst { users, emailChangeTokens } = tables;\n\n\tconst tokenHash = await hashToken(token);\n\n\t// 1. Find token record\n\tconst [tokenRecord] = await db\n\t\t.select()\n\t\t.from(emailChangeTokens)\n\t\t.where(eq(emailChangeTokens.tokenHash, tokenHash))\n\t\t.limit(1);\n\n\tif (!tokenRecord) {\n\t\treturn { success: false, error: 'Invalid or expired token' };\n\t}\n\n\t// 2. Check expiration\n\tif (tokenRecord.expiresAt < Date.now()) {\n\t\t// Clean up expired token\n\t\tawait db.delete(emailChangeTokens).where(eq(emailChangeTokens.id, tokenRecord.id));\n\t\treturn { success: false, error: 'Invalid or expired token' };\n\t}\n\n\t// 3. Update user's email\n\tawait db\n\t\t.update(users)\n\t\t.set({\n\t\t\temail: tokenRecord.newEmail,\n\t\t\tupdatedAt: new Date(),\n\t\t})\n\t\t.where(eq(users.id, tokenRecord.userId));\n\n\t// 4. Delete the token record\n\tawait db.delete(emailChangeTokens).where(eq(emailChangeTokens.id, tokenRecord.id));\n\n\treturn { success: true };\n}\n\ninterface CancelEmailChangeParams {\n\ttoken: string;\n\tdb: DrizzleDB;\n\ttables: Pick<EmailChangeTables, 'emailChangeTokens'>;\n}\n\ninterface CancelEmailChangeResult {\n\tsuccess: boolean;\n\terror?: string;\n}\n\n/**\n * Cancel email change - validates cancel token and deletes pending change\n */\nexport async function cancelEmailChange(\n\tparams: CancelEmailChangeParams\n): Promise<CancelEmailChangeResult> {\n\tconst { token, db, tables } = params;\n\tconst { emailChangeTokens } = tables;\n\n\tconst tokenHash = await hashToken(token);\n\n\t// 1. Find token record by cancel token\n\tconst [tokenRecord] = await db\n\t\t.select()\n\t\t.from(emailChangeTokens)\n\t\t.where(eq(emailChangeTokens.cancelTokenHash, tokenHash))\n\t\t.limit(1);\n\n\tif (!tokenRecord) {\n\t\treturn { success: false, error: 'Invalid or expired token' };\n\t}\n\n\t// 2. Delete the token record (cancels the pending change)\n\tawait db.delete(emailChangeTokens).where(eq(emailChangeTokens.id, tokenRecord.id));\n\n\treturn { success: true };\n}\n","/**\n * Unified API Key utilities\n *\n * Keys use `syn_` prefix + 32 hex chars (36 total).\n * Keys are hashed with SHA-256 for storage — never stored in retrievable form.\n */\n\nconst API_KEY_PREFIX = 'syn_';\nconst KEY_HEX_LENGTH = 32;\nconst KEY_TOTAL_LENGTH = API_KEY_PREFIX.length + KEY_HEX_LENGTH; // 36\n\n/**\n * Generate a new API key: syn_ + 32 random hex characters\n */\nexport function generateApiKey(): string {\n\tconst bytes = new Uint8Array(KEY_HEX_LENGTH / 2);\n\tcrypto.getRandomValues(bytes);\n\tconst hex = Array.from(bytes)\n\t\t.map((b) => b.toString(16).padStart(2, '0'))\n\t\t.join('');\n\treturn `${API_KEY_PREFIX}${hex}`;\n}\n\n/**\n * Hash an API key using SHA-256 for secure storage/lookup\n */\nexport async function hashApiKey(key: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(key);\n\tconst hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\treturn Array.from(new Uint8Array(hashBuffer))\n\t\t.map((b) => b.toString(16).padStart(2, '0'))\n\t\t.join('');\n}\n\n/**\n * Extract display prefix from a key (first 12 chars: syn_ + 8 hex)\n */\nexport function getKeyPrefix(key: string): string {\n\treturn key.slice(0, API_KEY_PREFIX.length + 8);\n}\n\n/**\n * Validate API key format: syn_ + 32 hex chars\n */\nexport function isValidApiKeyFormat(key: string): boolean {\n\tif (key.length !== KEY_TOTAL_LENGTH) return false;\n\tif (!key.startsWith(API_KEY_PREFIX)) return false;\n\tconst hex = key.slice(API_KEY_PREFIX.length);\n\treturn /^[a-f0-9]+$/.test(hex);\n}\n","// CSRF protection via Origin header validation\n// Per OWASP 2024 best practices\n\nimport logger from '../lib/logger';\n\n/**\n * Validate Origin header for state-changing requests\n * Prevents CSRF attacks by ensuring requests come from same origin\n *\n * @param request - The incoming request\n * @param allowedOrigins - List of allowed origins (e.g., ['https://app.example.com'])\n * @returns True if valid, false if suspicious\n */\nexport function validateOrigin(\n\trequest: Request,\n\tallowedOrigins: string[],\n\tpagesPattern?: string\n): boolean {\n\tconst method = request.method.toUpperCase();\n\n\t// Only validate state-changing requests\n\tif (!['POST', 'PUT', 'DELETE', 'PATCH'].includes(method)) {\n\t\treturn true;\n\t}\n\n\tconst origin = request.headers.get('origin');\n\tconst referer = request.headers.get('referer');\n\n\t// If no Origin header, check Referer as fallback\n\tconst sourceHeader = origin || referer;\n\n\tif (!sourceHeader) {\n\t\t// Missing headers - BLOCK for security (OWASP 2024 recommendation)\n\t\tlogger.warn('CSRF: Missing Origin/Referer header - BLOCKED', {\n\t\t\ttype: 'security',\n\t\t\tevent: 'csrf_missing_headers',\n\t\t\tseverity: 'high',\n\t\t\tmethod,\n\t\t\turl: request.url,\n\t\t});\n\t\treturn false; // Block requests without Origin/Referer\n\t}\n\n\t// Extract origin from header\n\tlet sourceOrigin: string | null = origin || null;\n\n\t// If no Origin, try to extract from Referer\n\tif (!sourceOrigin && referer) {\n\t\ttry {\n\t\t\tsourceOrigin = new URL(referer).origin;\n\t\t} catch {\n\t\t\t// Malformed Referer URL - reject for security\n\t\t\tlogger.warn('CSRF: Malformed Referer header - BLOCKED', {\n\t\t\t\ttype: 'security',\n\t\t\t\tevent: 'csrf_malformed_referer',\n\t\t\t\tseverity: 'high',\n\t\t\t\treferer,\n\t\t\t\tmethod,\n\t\t\t\turl: request.url,\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!sourceOrigin) {\n\t\treturn false;\n\t}\n\n\t// Check if origin is in allowed list, is a Cloudflare Pages preview URL, or Capacitor app\n\tconst isAllowed =\n\t\tallowedOrigins.some((allowed) => sourceOrigin === allowed) ||\n\t\tisCloudflarePreviewUrl(sourceOrigin, pagesPattern) ||\n\t\tisCapacitorApp(sourceOrigin);\n\n\tif (!isAllowed) {\n\t\tlogger.warn('CSRF: Origin mismatch', {\n\t\t\ttype: 'security',\n\t\t\tevent: 'csrf_origin_mismatch',\n\t\t\tseverity: 'high',\n\t\t\tsource: sourceOrigin,\n\t\t\tallowed: allowedOrigins,\n\t\t\tmethod,\n\t\t\turl: request.url,\n\t\t});\n\t}\n\n\treturn isAllowed;\n}\n\n/**\n * Get allowed origins from environment\n */\nexport function getAllowedOrigins(appUrl: string): string[] {\n\tconst origins = [appUrl];\n\n\t// Always add localhost variants for local development\n\t// These are safe because we use SameSite cookies + proper CORS\n\torigins.push(\n\t\t'http://localhost:5173',\n\t\t'http://localhost:5174',\n\t\t'http://127.0.0.1:5173',\n\t\t'http://127.0.0.1:5174'\n\t);\n\n\treturn origins;\n}\n\n/**\n * Check if origin is a Cloudflare Pages preview URL for this project.\n * Uses CORS_PAGES_PATTERN env var (e.g., \"your-app.pages.dev\") to match preview deployments.\n * If no pattern is configured, returns false (no preview URLs allowed by default).\n */\nexport function isCloudflarePreviewUrl(origin: string, pagesPattern?: string): boolean {\n\tif (!pagesPattern) return false;\n\treturn (\n\t\torigin.match(new RegExp(`^https:\\\\/\\\\/[a-z0-9-]+\\\\.${pagesPattern.replace('.', '\\\\.')}$`)) !==\n\t\tnull\n\t);\n}\n\n/**\n * Check if origin is a Capacitor mobile app\n * Allows capacitor:// and ionic:// protocols used by iOS/Android apps\n */\nexport function isCapacitorApp(origin: string): boolean {\n\treturn (\n\t\torigin === 'capacitor://localhost' ||\n\t\torigin === 'ionic://localhost' ||\n\t\torigin === 'http://localhost' // Android WebView\n\t);\n}\n","/**\n * Mobile authentication utilities\n * Implements PKCE (RFC 7636) for secure OAuth on native apps\n */\n\n/**\n * Generate a cryptographically random code verifier for PKCE\n * @returns Code verifier string (43-128 chars, URL-safe)\n */\nexport function generateCodeVerifier(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn base64UrlEncode(array);\n}\n\n/**\n * Generate S256 code challenge from verifier\n * @param verifier - Code verifier string\n * @returns Base64URL-encoded SHA256 hash\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(verifier);\n\tconst hash = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(new Uint8Array(hash));\n}\n\n/**\n * Verify that a code verifier matches a code challenge\n * @param verifier - The code verifier from the token request\n * @param challenge - The code challenge from the authorization request\n * @returns True if verifier hashes to challenge\n */\nexport async function verifyCodeChallenge(verifier: string, challenge: string): Promise<boolean> {\n\tconst computed = await generateCodeChallenge(verifier);\n\treturn computed === challenge;\n}\n\n/**\n * Check if a URI is a deep link (custom scheme)\n * Deep links are used by native mobile apps for OAuth callbacks\n * @param uri - The redirect URI to check\n * @returns True if URI uses a custom scheme (not http/https)\n */\nexport function isDeepLinkUri(uri: string): boolean {\n\ttry {\n\t\tconst url = new URL(uri);\n\t\tconst scheme = url.protocol.replace(':', '');\n\t\tif (scheme === 'http' || scheme === 'https') {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Base64URL encode (RFC 4648)\n */\nfunction base64UrlEncode(data: Uint8Array): string {\n\tconst base64 = btoa(String.fromCharCode(...data));\n\treturn base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n","// Auth middleware for Hono - protects routes requiring authentication\n// Updated to use Drizzle ORM\n\nimport { createMiddleware } from 'hono/factory';\nimport { eq, and, gt } from 'drizzle-orm';\nimport type { Env, Variables } from '../types';\nimport { getAuthContext } from '../factory';\nimport { generateFingerprint } from '../core/fingerprint';\nimport { clearSessionCookie, getSessionCookieName } from '../core/cookies';\nimport { refreshSession } from '../core/session';\nimport logger from '../lib/logger';\nimport { problems } from '../lib/problem-json';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype SessionsTable = any;\n\n/**\n * Get session from cookie header\n * Returns session data if valid, null otherwise\n * Validates fingerprint to detect session hijacking\n */\nasync function getSessionFromCookie(\n\tdb: DrizzleDB,\n\tsessions: SessionsTable,\n\tcookieHeader: string,\n\trequest: Request,\n\tenv?: { ENVIRONMENT?: string }\n) {\n\t// Use environment-specific cookie name (production: sessionId, staging: staging_sessionId)\n\tconst cookieName = getSessionCookieName(env);\n\tconst cookiePattern = new RegExp(`${cookieName}=([^;]+)`);\n\tconst sessionIdMatch = cookieHeader.match(cookiePattern);\n\tif (!sessionIdMatch) return null;\n\n\tconst sessionId = sessionIdMatch[1];\n\n\tconst foundSessions = await db\n\t\t.select({\n\t\t\tuserId: sessions.userId,\n\t\t\texpiresAt: sessions.expiresAt,\n\t\t\tfingerprint: sessions.fingerprint,\n\t\t})\n\t\t.from(sessions)\n\t\t.where(and(eq(sessions.id, sessionId), gt(sessions.expiresAt, Date.now())))\n\t\t.limit(1);\n\n\tif (foundSessions.length === 0) return null;\n\n\tconst session = foundSessions[0];\n\n\t/**\n\t * Session Validation Strategy (2025 Best Practices - OWASP Aligned)\n\t *\n\t * Challenge: Server-Side Rendering (SSR) requests have different user agents:\n\t * - Browser requests: \"Mozilla/5.0...\" (can be fingerprinted)\n\t * - SSR requests: \"node\" (local) or missing (Cloudflare Pages) (cannot be fingerprinted)\n\t *\n\t * Security Trade-off:\n\t * - Client requests: Full fingerprint validation (detects hijacking)\n\t * - SSR requests: Cannot fingerprint (technical limitation, not security bypass)\n\t *\n\t * Compensating Controls for SSR:\n\t * 1. IP address validation (detect major location changes)\n\t * 2. Session expiration (7 days, sliding window)\n\t * 3. CSRF protection (all state-changing requests)\n\t * 4. Separate logging (anomaly detection)\n\t *\n\t * Per OWASP: \"Multi-layered approach\" acknowledging fingerprinting isn't foolproof\n\t */\n\tconst userAgent = request.headers.get('user-agent') || '';\n\tconst cfWorker = request.headers.get('cf-worker') || '';\n\tconst clientIp =\n\t\trequest.headers.get('cf-connecting-ip') || request.headers.get('x-real-ip') || '';\n\n\t// Detect SSR: Local dev (user-agent: \"node\") OR SvelteKit SSR OR Cloudflare Pages (cf-worker header)\n\tconst isLocalSSR = userAgent.toLowerCase().includes('node');\n\tconst isSvelteKitSSR = userAgent.toLowerCase().includes('sveltekit');\n\tconst isCloudflareSSR = !userAgent && cfWorker.includes('.pages.dev');\n\tconst isSSR = isLocalSSR || isSvelteKitSSR || isCloudflareSSR;\n\n\t// For SSR requests, use IP validation instead of fingerprint\n\tif (isSSR) {\n\t\tlogger.info('SSR request detected, using IP validation', {\n\t\t\ttype: 'security',\n\t\t\tevent: 'ssr_session_validation',\n\t\t\tsessionId: sessionId.slice(0, 8),\n\t\t\tuserId: session.userId,\n\t\t\tclientIp,\n\t\t\tcfWorker,\n\t\t\tisLocalSSR,\n\t\t\tisSvelteKitSSR,\n\t\t\tisCloudflareSSR,\n\t\t});\n\n\t\t// Validate IP hasn't changed dramatically (optional - can be strict or lenient)\n\t\t// For now, just log - can tighten later if abuse detected\n\t} else {\n\t\t// Client request - fingerprint validation DISABLED\n\t\t// User-Agent is too volatile: DevTools device mode, extensions, browser updates\n\t\t// Security is maintained via: session expiration, CSRF protection, secure cookies, IP logging\n\t\t//\n\t\t// If re-enabling fingerprinting, consider:\n\t\t// - Only using Accept-Language (more stable than User-Agent)\n\t\t// - Logging mismatches instead of invalidating sessions\n\t\t// - Allowing a grace period for fingerprint changes\n\t\tif (session.fingerprint) {\n\t\t\tconst currentFingerprint = await generateFingerprint(request);\n\t\t\tif (session.fingerprint !== currentFingerprint) {\n\t\t\t\t// Log for monitoring but don't invalidate - too many false positives\n\t\t\t\tlogger.info('Session fingerprint changed (not enforced)', {\n\t\t\t\t\ttype: 'security',\n\t\t\t\t\tevent: 'fingerprint_changed',\n\t\t\t\t\tsessionId: sessionId.slice(0, 8),\n\t\t\t\t\tuserId: session.userId,\n\t\t\t\t\tclientIp,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tuserId: session.userId,\n\t\texpiresAt: session.expiresAt,\n\t};\n}\n\n/**\n * Get session from Authorization Bearer token header\n * Returns session data if valid, null otherwise\n */\nasync function getSessionFromBearerToken(db: DrizzleDB, sessions: SessionsTable, authHeader: string) {\n\tif (!authHeader.startsWith('Bearer ')) return null;\n\n\tconst sessionId = authHeader.slice(7); // Remove 'Bearer ' prefix\n\n\tconst foundSessions = await db\n\t\t.select({\n\t\t\tuserId: sessions.userId,\n\t\t\texpiresAt: sessions.expiresAt,\n\t\t})\n\t\t.from(sessions)\n\t\t.where(and(eq(sessions.id, sessionId), gt(sessions.expiresAt, Date.now())))\n\t\t.limit(1);\n\n\tif (foundSessions.length === 0) return null;\n\n\tconst session = foundSessions[0];\n\n\t// Note: No fingerprint validation for token-based auth (mobile clients)\n\t// Security relies on: HTTPS, secure token storage, session expiry\n\n\t// Refresh session expiration (sliding window) - same as cookie-based auth\n\tawait refreshSession(db, { sessions }, sessionId);\n\n\treturn {\n\t\tuserId: session.userId,\n\t\texpiresAt: session.expiresAt,\n\t};\n}\n\n/**\n * Auth middleware - ensures user is authenticated\n * Sets userId in context if session is valid\n */\nexport const requireAuth = createMiddleware<{ Bindings: Env; Variables: Variables }>(\n\tasync (c, next) => {\n\t\tconst { db, schema } = getAuthContext(c);\n\n\t\t// Check Bearer token first (for mobile clients)\n\t\tconst authHeader = c.req.header('Authorization');\n\t\tif (authHeader) {\n\t\t\tconst session = await getSessionFromBearerToken(db, schema.sessions, authHeader);\n\t\t\tif (session) {\n\t\t\t\tc.set('userId', session.userId);\n\t\t\t\tawait next();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to cookie-based auth (for web/extension)\n\t\tconst cookieHeader = c.req.header('cookie');\n\n\t\tif (!cookieHeader) {\n\t\t\treturn problems.unauthorized(c, 'Please log in to continue');\n\t\t}\n\n\t\tconst session = await getSessionFromCookie(db, schema.sessions, cookieHeader, c.req.raw, c.env);\n\n\t\tif (!session) {\n\t\t\t// Clear the stale/invalid cookie so the browser stops sending it\n\t\t\t// This fixes the infinite loop when fingerprint mismatch invalidates the session\n\t\t\t// but the cookie persists, causing repeated auth failures\n\t\t\tc.header('Set-Cookie', clearSessionCookie(c.env));\n\t\t\treturn problems.unauthorized(c, 'Your session has expired. Please log in again.');\n\t\t}\n\n\t\tc.set('userId', session.userId);\n\n\t\tawait next();\n\t}\n);\n\n/**\n * Optional auth middleware - sets userId if session exists, continues either way\n */\nexport const optionalAuth = createMiddleware<{ Bindings: Env; Variables: Variables }>(\n\tasync (c, next) => {\n\t\tconst { db, schema } = getAuthContext(c);\n\n\t\t// Check Bearer token first (for mobile clients)\n\t\tconst authHeader = c.req.header('Authorization');\n\t\tif (authHeader) {\n\t\t\tconst session = await getSessionFromBearerToken(db, schema.sessions, authHeader);\n\t\t\tif (session) {\n\t\t\t\tc.set('userId', session.userId);\n\t\t\t\tawait next();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to cookie-based auth (for web/extension)\n\t\tconst cookieHeader = c.req.header('cookie');\n\n\t\tif (cookieHeader) {\n\t\t\tconst session = await getSessionFromCookie(db, schema.sessions, cookieHeader, c.req.raw, c.env);\n\t\t\tif (session) {\n\t\t\t\tc.set('userId', session.userId);\n\t\t\t}\n\t\t}\n\n\t\t// Continue regardless of auth status (userId may be undefined)\n\t\tawait next();\n\t}\n);\n","// src/factory.ts\n\nimport type { Context } from 'hono';\nimport type { PgTable } from 'drizzle-orm/pg-core';\nimport type { InferSelectModel, InferInsertModel } from 'drizzle-orm';\n\n/**\n * Base type for auth tables - any Postgres table\n */\ntype AuthTable = PgTable;\n\n/**\n * Schema interface that consumers must provide.\n * Each property must be a Drizzle PgTable with the expected columns.\n * The CLI generates these tables in the consumer's project.\n */\nexport interface AuthSchema {\n users: AuthTable;\n sessions: AuthTable;\n user2faMethods: AuthTable;\n userBackupCodes: AuthTable;\n userTrustedDevices: AuthTable;\n emailVerificationTokens: AuthTable;\n passwordResetTokens: AuthTable;\n emailChangeTokens: AuthTable;\n emailEvents: AuthTable;\n failedLoginAttempts: AuthTable;\n securityAuditLog: AuthTable;\n oauthAccounts: AuthTable;\n}\n\n/**\n * Infer model types from a consumer's schema.\n * These utilities let consumers extract types from their specific tables.\n *\n * @example\n * ```typescript\n * import { createAuth, type InferUser } from '@syncello/auth';\n *\n * const auth = createAuth({ db, schema, getEnv });\n * type User = InferUser<typeof auth.schema>;\n * ```\n */\nexport type InferUser<TSchema extends AuthSchema> = InferSelectModel<TSchema['users']>;\nexport type InferNewUser<TSchema extends AuthSchema> = InferInsertModel<TSchema['users']>;\nexport type InferSession<TSchema extends AuthSchema> = InferSelectModel<TSchema['sessions']>;\nexport type InferNewSession<TSchema extends AuthSchema> = InferInsertModel<TSchema['sessions']>;\n\n/**\n * Database interface - accepts any Drizzle Postgres database.\n * Uses duck typing for flexibility across different Drizzle configurations.\n */\nexport interface AuthDatabase {\n select: <T extends PgTable>(from?: T) => unknown;\n insert: <T extends PgTable>(into: T) => unknown;\n update: <T extends PgTable>(table: T) => unknown;\n delete: <T extends PgTable>(from: T) => unknown;\n query: Record<string, unknown>;\n}\n\n/**\n * Configuration for createAuth\n */\nexport interface AuthConfig<TEnv = Record<string, unknown>> {\n /** Drizzle database instance */\n db: AuthDatabase;\n /** Schema tables from generated auth.ts */\n schema: AuthSchema;\n /** Environment variables accessor */\n getEnv: (c: Context) => TEnv;\n}\n\n/**\n * Auth context available in all auth operations.\n * Preserves the TEnv generic for type-safe environment access.\n */\nexport interface AuthContext<TEnv = Record<string, unknown>> {\n db: AuthDatabase;\n schema: AuthSchema;\n env: TEnv;\n}\n\n/**\n * Creates an auth instance with injected database and schema.\n * This follows the adapter pattern - consumers pass their db/schema TO the library.\n *\n * @example\n * ```typescript\n * import { createAuth } from '@syncello/auth';\n * import { db } from './db';\n * import * as schema from './db/schema';\n *\n * const auth = createAuth({\n * db,\n * schema: {\n * users: schema.users,\n * sessions: schema.sessions,\n * user2faMethods: schema.user2faMethods,\n * userBackupCodes: schema.userBackupCodes,\n * userTrustedDevices: schema.userTrustedDevices,\n * emailVerificationTokens: schema.emailVerificationTokens,\n * passwordResetTokens: schema.passwordResetTokens,\n * emailChangeTokens: schema.emailChangeTokens,\n * emailEvents: schema.emailEvents,\n * failedLoginAttempts: schema.failedLoginAttempts,\n * securityAuditLog: schema.securityAuditLog,\n * oauthAccounts: schema.oauthAccounts,\n * },\n * getEnv: (c) => c.env,\n * });\n *\n * // Use context in middleware/handlers\n * const ctx = auth.getContext(c);\n * const user = await ctx.db.query.users.findFirst(...);\n * ```\n */\nexport function createAuth<TEnv = Record<string, unknown>>(config: AuthConfig<TEnv>) {\n const { db, schema, getEnv } = config;\n\n // Create context getter for use in routes and middleware\n const getContext = (c: Context): AuthContext<TEnv> => ({\n db,\n schema,\n env: getEnv(c),\n });\n\n return {\n db,\n schema,\n getContext,\n };\n}\n\nexport type Auth<TEnv = Record<string, unknown>> = ReturnType<typeof createAuth<TEnv>>;\n\n/**\n * Creates a middleware that injects AuthContext into the Hono context.\n * Apply this middleware before mounting auth routes.\n *\n * @example\n * ```typescript\n * import { createAuth, createAuthMiddleware } from '@syncello/auth';\n *\n * const auth = createAuth({ db, schema, getEnv: (c) => c.env });\n * const authMiddleware = createAuthMiddleware(auth);\n *\n * app.use('/api/auth/*', authMiddleware);\n * app.route('/api/auth', authRoutes);\n * ```\n */\nexport function createAuthMiddleware<TEnv = Record<string, unknown>>(\n auth: Auth<TEnv>\n): (c: Context, next: () => Promise<void>) => Promise<void> {\n return async (c: Context, next: () => Promise<void>) => {\n const ctx = auth.getContext(c);\n c.set('authContext', ctx);\n c.set('db', auth.db);\n await next();\n };\n}\n\n/**\n * Helper to get AuthContext from Hono context.\n * Use this in route handlers to access db, schema, and env.\n *\n * @example\n * ```typescript\n * import { getAuthContext } from '@syncello/auth';\n *\n * const handler = async (c) => {\n * const { db, schema, env } = getAuthContext(c);\n * const user = await db.select().from(schema.users).where(...);\n * };\n * ```\n */\nexport function getAuthContext<TEnv = Record<string, unknown>>(\n c: Context\n): AuthContext<TEnv> {\n const ctx = c.get('authContext') as AuthContext<TEnv> | undefined;\n if (!ctx) {\n throw new Error(\n 'AuthContext not found. Did you apply createAuthMiddleware before the auth routes?'\n );\n }\n return ctx;\n}\n","/**\n * RFC 9457 - Problem Details for HTTP APIs\n *\n * Provides standardized error responses in application/problem+json format\n * with traceability support (traceId, requestId).\n *\n * Security: Follows OWASP API Security 2023 guidance:\n * - Never expose stack traces or implementation details\n * - Use generic error messages in production\n * - Include traceId/requestId for debugging without leaking sensitive data\n *\n * @see https://www.rfc-editor.org/rfc/rfc9457.html\n * @see https://owasp.org/API-Security/editions/2023/en/0xa8-security-misconfiguration/\n */\n\nimport type { Context } from 'hono';\nimport logger, { logError } from './logger';\n\n/**\n * RFC 9457 Problem Details interface\n * All fields are optional per spec, but we require type/title/status for consistency\n */\nexport interface ProblemDetails {\n\t/** URI identifying the problem type (defaults to about:blank) */\n\ttype: string;\n\t/** Short, human-readable summary of the problem type */\n\ttitle: string;\n\t/** HTTP status code */\n\tstatus: number;\n\t/** Human-readable explanation specific to this occurrence */\n\tdetail?: string;\n\t/** URI identifying the specific occurrence of the problem */\n\tinstance?: string;\n\t/** OpenTelemetry trace ID (32-char hex) for distributed tracing */\n\ttraceId?: string;\n\t/** Request ID from X-Request-ID header for correlation */\n\trequestId?: string;\n\t/** Additional extension members (validation errors, etc.) */\n\t[key: string]: unknown;\n}\n\n/**\n * Common problem types for the API\n * Using relative URIs - update these to point to your documentation\n */\nexport const ProblemTypes = {\n\t// 4xx Client Errors\n\tBAD_REQUEST: '/errors/bad-request',\n\tUNAUTHORIZED: '/errors/unauthorized',\n\tFORBIDDEN: '/errors/forbidden',\n\tNOT_FOUND: '/errors/not-found',\n\tCONFLICT: '/errors/conflict',\n\tGONE: '/errors/gone',\n\tVALIDATION_ERROR: '/errors/validation-error',\n\tRATE_LIMIT_EXCEEDED: '/errors/rate-limit-exceeded',\n\n\t// 5xx Server Errors\n\tINTERNAL_ERROR: '/errors/internal-error',\n\tSERVICE_UNAVAILABLE: '/errors/service-unavailable',\n} as const;\n\n/**\n * Generate a trace ID in OpenTelemetry format (32-character hex string)\n * Uses Web Crypto API for cryptographically secure randomness\n */\nexport function generateTraceId(): string {\n\tconst bytes = crypto.getRandomValues(new Uint8Array(16));\n\treturn Array.from(bytes)\n\t\t.map((b) => b.toString(16).padStart(2, '0'))\n\t\t.join('');\n}\n\n/**\n * Create a Problem Details object with automatic traceId and requestId\n */\nexport function createProblemDetails(\n\ttype: string,\n\ttitle: string,\n\tstatus: number,\n\toptions?: {\n\t\tdetail?: string;\n\t\tinstance?: string;\n\t\ttraceId?: string;\n\t\trequestId?: string;\n\t\textensions?: Record<string, unknown>;\n\t}\n): ProblemDetails {\n\tconst problem: ProblemDetails = {\n\t\ttype,\n\t\ttitle,\n\t\tstatus,\n\t};\n\n\tif (options?.detail) {\n\t\tproblem.detail = options.detail;\n\t}\n\n\tif (options?.instance) {\n\t\tproblem.instance = options.instance;\n\t}\n\n\t// Always include traceId for debugging\n\tproblem.traceId = options?.traceId || generateTraceId();\n\n\t// Include requestId if provided\n\tif (options?.requestId) {\n\t\tproblem.requestId = options.requestId;\n\t}\n\n\t// Add any extension members\n\tif (options?.extensions) {\n\t\tObject.assign(problem, options.extensions);\n\t}\n\n\treturn problem;\n}\n\n/**\n * Return a Problem Details JSON response from a Hono context\n * Sets the correct Content-Type header (application/problem+json)\n */\nexport function problemJson(c: Context, problem: ProblemDetails) {\n\t// Log the error for observability (no sensitive data)\n\tlogger.warn('API error response', {\n\t\ttype: problem.type,\n\t\tstatus: problem.status,\n\t\ttraceId: problem.traceId,\n\t\trequestId: problem.requestId,\n\t\tpath: c.req.path,\n\t\tmethod: c.req.method,\n\t});\n\n\treturn c.json(problem, problem.status as 400 | 401 | 403 | 404 | 409 | 410 | 429 | 500 | 503, {\n\t\t'Content-Type': 'application/problem+json',\n\t});\n}\n\n/**\n * Helper function to return common problem types\n */\nexport const problems = {\n\t/**\n\t * 400 Bad Request - Generic client error\n\t */\n\tbadRequest(c: Context, detail?: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.BAD_REQUEST, 'Bad Request', 400, {\n\t\t\t\tdetail: detail || 'The request could not be understood by the server',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 400 Bad Request - Validation errors with field-specific details\n\t */\n\tvalidationError(c: Context, detail: string, errors?: Array<{ field: string; message: string }>) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.VALIDATION_ERROR, 'Validation Error', 400, {\n\t\t\t\tdetail,\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t\textensions: errors ? { errors } : undefined,\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 401 Unauthorized - Authentication required\n\t */\n\tunauthorized(c: Context, detail?: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.UNAUTHORIZED, 'Unauthorized', 401, {\n\t\t\t\tdetail: detail || 'Authentication is required to access this resource',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 403 Forbidden - Insufficient permissions\n\t */\n\tforbidden(c: Context, detail?: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.FORBIDDEN, 'Forbidden', 403, {\n\t\t\t\tdetail: detail || 'You do not have permission to access this resource',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 404 Not Found\n\t */\n\tnotFound(c: Context, detail?: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.NOT_FOUND, 'Not Found', 404, {\n\t\t\t\tdetail: detail || 'The requested resource was not found',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 409 Conflict - Resource conflict (e.g., duplicate email)\n\t */\n\tconflict(c: Context, detail: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.CONFLICT, 'Conflict', 409, {\n\t\t\t\tdetail,\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 410 Gone - Resource is no longer available\n\t */\n\tgone(c: Context, detail?: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.GONE, 'Gone', 410, {\n\t\t\t\tdetail: detail || 'The requested resource is no longer available',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 429 Too Many Requests - Rate limit exceeded\n\t */\n\trateLimitExceeded(c: Context, retryAfter?: number) {\n\t\tconst extensions = retryAfter ? { retryAfter } : undefined;\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.RATE_LIMIT_EXCEEDED, 'Rate Limit Exceeded', 429, {\n\t\t\t\tdetail: retryAfter\n\t\t\t\t\t? `Too many requests. Please retry after ${retryAfter} seconds`\n\t\t\t\t\t: 'Too many requests. Please slow down',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t\textensions,\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 500 Internal Server Error - Generic server error\n\t * SECURITY: Never include error details or stack traces\n\t */\n\tinternalError(c: Context, error?: Error) {\n\t\t// Use trace ID from context if available, otherwise generate new one\n\t\tconst traceId = c.get('traceId') || generateTraceId();\n\n\t\t// Log the full error server-side (with stack trace)\n\t\tif (error) {\n\t\t\tlogError(error, {\n\t\t\t\tcontext: 'internal_error',\n\t\t\t\tpath: c.req.path,\n\t\t\t\tmethod: c.req.method,\n\t\t\t\ttraceId,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t});\n\t\t}\n\n\t\t// Return generic message to client (OWASP best practice)\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.INTERNAL_ERROR, 'Internal Server Error', 500, {\n\t\t\t\tdetail: 'An unexpected error occurred. Please try again later',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\ttraceId,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t})\n\t\t);\n\t},\n\n\t/**\n\t * 503 Service Unavailable - Service is temporarily unavailable\n\t */\n\tserviceUnavailable(c: Context, detail?: string) {\n\t\treturn problemJson(\n\t\t\tc,\n\t\t\tcreateProblemDetails(ProblemTypes.SERVICE_UNAVAILABLE, 'Service Unavailable', 503, {\n\t\t\t\tdetail: detail || 'The service is temporarily unavailable. Please try again later',\n\t\t\t\tinstance: `${c.req.method} ${c.req.path}`,\n\t\t\t\trequestId: c.get('requestId'),\n\t\t\t\ttraceId: c.get('traceId'),\n\t\t\t})\n\t\t);\n\t},\n};\n","// CSRF protection middleware for Hono\n\nimport { createMiddleware } from 'hono/factory';\nimport { validateOrigin, getAllowedOrigins } from '../core/csrf';\nimport type { Env, Variables } from '../types';\nimport { problems } from '../lib/problem-json';\nimport logger from '../lib/logger';\n\n/**\n * CSRF middleware - validates Origin header for state-changing requests\n * Prevents CSRF attacks by ensuring requests come from allowed origins\n */\nexport const csrf = createMiddleware<{ Bindings: Env; Variables: Variables }>(async (c, next) => {\n\tconst method = c.req.method;\n\tconst path = c.req.path;\n\n\t// Skip CSRF validation for webhook endpoints (they use signature verification or API key instead)\n\tconst isWebhook = path.startsWith('/v1/webhooks/');\n\tif (isWebhook) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Skip CSRF for MCP endpoint (uses Bearer token auth, not browser sessions)\n\t// MCP clients don't send Origin headers since they're not browsers\n\tconst isMcpEndpoint = path === '/v1/mcp';\n\tif (isMcpEndpoint) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Skip CSRF for MCP OAuth endpoints (uses client credentials / PKCE, not browser sessions)\n\tconst isOAuthMcpEndpoint = path.startsWith('/v1/oauth/');\n\tif (isOAuthMcpEndpoint) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Skip CSRF for public REST API (uses JWT Bearer token auth, not browser sessions)\n\t// External API clients don't send Origin headers since they're not browsers\n\tconst isToolsApi = path.startsWith('/v1/tools/') && !path.startsWith('/v1/tools/catalog');\n\tif (isToolsApi) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Skip CSRF for admin API (uses Bearer token auth, not browser sessions)\n\t// Admin clients (CLI, scripts) don't send Origin headers since they're not browsers.\n\tconst isAdminApi = path.startsWith('/v1/admin/');\n\tif (isAdminApi) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Skip CSRF for mobile auth endpoints\n\t// Native mobile apps don't send Origin headers and can't do CSRF attacks\n\t// Security is maintained via: rate limiting, Turnstile, secure token storage\n\tconst isMobileAuthEndpoint =\n\t\tpath === '/v1/auth/login' || path === '/v1/auth/signup' || path === '/v1/auth/refresh';\n\n\tconst authHeader = c.req.header('Authorization');\n\tconst hasBearerToken = authHeader?.startsWith('Bearer ');\n\tconst hasNoOrigin = !c.req.header('origin') && !c.req.header('referer');\n\n\t// Allow if: mobile auth endpoint + no origin (native app) OR has Bearer token\n\tif ((isMobileAuthEndpoint && hasNoOrigin) || hasBearerToken) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Skip CSRF for internal sync routes (called by Durable Objects, not browsers)\n\t// These are server-to-server requests that don't have Origin headers\n\t// SECURITY: Protected by internalAuth middleware (shared secret + User-Agent validation)\n\t// See: apps/api/src/middleware/internal-auth.ts\n\tconst isInternalSync = path.startsWith('/v1/internal/sync/');\n\tif (isInternalSync) {\n\t\tawait next();\n\t\treturn;\n\t}\n\n\t// Only validate state-changing requests\n\tif (['POST', 'PUT', 'DELETE', 'PATCH'].includes(method)) {\n\t\tconst appUrl = c.env.APP_URL || 'http://localhost:5173';\n\t\tconst allowedOrigins = getAllowedOrigins(appUrl);\n\n\t\t// Convert Hono request to standard Request\n\t\tconst request = c.req.raw;\n\n\t\t// Debug logging for test endpoints\n\t\tconst isTestEndpoint = c.req.path.startsWith('/test');\n\t\tif (isTestEndpoint) {\n\t\t\tconst origin = request.headers.get('origin');\n\t\t\tconst referer = request.headers.get('referer');\n\t\t\tlogger.debug('CSRF validation for test endpoint', {\n\t\t\t\tpath: c.req.path,\n\t\t\t\tmethod,\n\t\t\t\torigin,\n\t\t\t\treferer,\n\t\t\t\tappUrl,\n\t\t\t\tallowedOrigins,\n\t\t\t});\n\t\t}\n\n\t\tconst pagesPattern = c.env.CORS_PAGES_PATTERN;\n\t\tif (!validateOrigin(request, allowedOrigins, pagesPattern)) {\n\t\t\treturn problems.forbidden(c, 'Request blocked for security reasons. Please try again.');\n\t\t}\n\t}\n\n\tawait next();\n});\n","// Rate limiting middleware factory for Hono\n// Uses Cloudflare KV for zero-database-cost rate limiting\n\nimport { createMiddleware } from 'hono/factory';\nimport { checkRateLimitKV } from '../lib/rate-limit-kv';\nimport type { Env, Variables } from '../types';\nimport type { Context } from 'hono';\nimport { problems } from '../lib/problem-json';\n\ntype RateLimitConfig = {\n\tidentifier: (c: Context<{ Bindings: Env; Variables: Variables }>) => Promise<string> | string;\n\taction: string;\n\tmaxAttempts: number;\n\twindowMs: number;\n};\n\n/**\n * Rate limit middleware factory\n * Uses KV-backed rate limiting (check + increment) to avoid database hits.\n *\n * Sets standard rate limit headers on all responses:\n * - X-RateLimit-Limit: Maximum requests allowed in window\n * - X-RateLimit-Remaining: Requests remaining in current window\n * - X-RateLimit-Reset: Unix timestamp when the window resets\n *\n * On 429 responses, also sets:\n * - Retry-After: Seconds until the rate limit resets\n *\n * @param config - Rate limit configuration\n * @returns Middleware that enforces rate limits\n */\nexport const rateLimit = (config: RateLimitConfig) =>\n\tcreateMiddleware<{ Bindings: Env; Variables: Variables }>(async (c, next) => {\n\t\tconst kv = c.env.RATE_LIMIT_KV;\n\t\tconst identifier = await config.identifier(c);\n\t\tconst key = `${config.action}:${identifier}`;\n\t\tconst windowSeconds = Math.ceil(config.windowMs / 1000);\n\n\t\tconst { allowed, remaining } = await checkRateLimitKV(\n\t\t\tkv,\n\t\t\tkey,\n\t\t\tconfig.maxAttempts,\n\t\t\twindowSeconds\n\t\t);\n\n\t\t// Calculate reset timestamp (approximate - window resets after windowSeconds from first request)\n\t\tconst resetTimestamp = Math.ceil(Date.now() / 1000) + windowSeconds;\n\n\t\t// Set rate limit headers on all responses\n\t\tc.header('X-RateLimit-Limit', config.maxAttempts.toString());\n\t\tc.header('X-RateLimit-Remaining', Math.max(0, remaining).toString());\n\t\tc.header('X-RateLimit-Reset', resetTimestamp.toString());\n\n\t\tif (!allowed) {\n\t\t\t// Add Retry-After header for 429 responses\n\t\t\tc.header('Retry-After', windowSeconds.toString());\n\t\t\treturn problems.rateLimitExceeded(c, windowSeconds);\n\t\t}\n\n\t\tawait next();\n\t});\n","/**\n * KV-Based Rate Limiting Helper\n *\n * Provides simple rate limiting using Cloudflare KV for stateless Workers.\n * Uses sliding window with automatic TTL-based cleanup.\n *\n * Usage:\n * ```typescript\n * import { checkRateLimitKV } from '../lib/rate-limit-kv';\n *\n * const { allowed, remaining } = await checkRateLimitKV(\n * c.env.OAUTH_STATE_KV,\n * `tape_validate:${userId}:${ip}`,\n * 10, // 10 attempts\n * 300 // per 5 minutes\n * );\n *\n * if (!allowed) {\n * return problems.rateLimitExceeded(c, remaining);\n * }\n * ```\n *\n * Key Format:\n * - `rate_limit:{resource}:{identifier}` - e.g., `rate_limit:tape_validate:user123:192.168.1.1`\n *\n * Best Practices:\n * - Use composite keys: resource + user ID + IP for granular control\n * - Set appropriate window based on sensitivity (300s = 5min, 3600s = 1hr)\n * - Log rate limit violations for security monitoring\n * - Return 429 status code with Retry-After header\n *\n * @see docs/auth/AUTH-BEST-PRACTICES.md - Rate limiting patterns\n * @see road-to-production/phase-03-oauth-integration.md - OAuth rate limiting\n */\n\n/// <reference types=\"@cloudflare/workers-types\" />\n\nimport logger from './logger';\n\n/**\n * Check if a request is within rate limit and increment counter\n *\n * @param kv - KV namespace binding\n * @param key - Unique key for rate limit bucket (e.g., \"tape_validate:user123:192.168.1.1\")\n * @param maxAttempts - Maximum number of attempts allowed in window\n * @param windowSeconds - Time window in seconds\n * @returns Object with allowed status and remaining attempts\n */\nexport async function checkRateLimitKV(\n\tkv: KVNamespace,\n\tkey: string,\n\tmaxAttempts: number,\n\twindowSeconds: number\n): Promise<{ allowed: boolean; remaining: number }> {\n\ttry {\n\t\tconst rateLimitKey = `rate_limit:${key}`;\n\n\t\t// Get current attempt count\n\t\tconst currentAttemptsStr = await kv.get(rateLimitKey);\n\t\tconst currentAttempts = currentAttemptsStr ? parseInt(currentAttemptsStr, 10) : 0;\n\n\t\t// Check if limit exceeded\n\t\tif (currentAttempts >= maxAttempts) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\tremaining: 0,\n\t\t\t};\n\t\t}\n\n\t\t// Increment counter with TTL\n\t\tconst newAttempts = currentAttempts + 1;\n\t\tawait kv.put(rateLimitKey, newAttempts.toString(), {\n\t\t\texpirationTtl: windowSeconds,\n\t\t});\n\n\t\treturn {\n\t\t\tallowed: true,\n\t\t\tremaining: maxAttempts - newAttempts,\n\t\t};\n\t} catch (error) {\n\t\t// On error, allow request but log the issue\n\t\t// Fail open to prevent blocking legitimate users due to KV issues\n\t\tlogger.error('Rate limit KV error, failing open', {\n\t\t\tkey,\n\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t});\n\n\t\treturn {\n\t\t\tallowed: true,\n\t\t\tremaining: maxAttempts,\n\t\t};\n\t}\n}\n\n/**\n * Reset rate limit for a specific key (e.g., after successful action)\n *\n * @param kv - KV namespace binding\n * @param key - Unique key for rate limit bucket\n */\nexport async function resetRateLimitKV(kv: KVNamespace, key: string): Promise<void> {\n\ttry {\n\t\tconst rateLimitKey = `rate_limit:${key}`;\n\t\tawait kv.delete(rateLimitKey);\n\t} catch (error) {\n\t\t// Log but don't throw - reset failures shouldn't block operations\n\t\tlogger.warn('Failed to reset rate limit', {\n\t\t\tkey,\n\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t});\n\t}\n}\n\n/**\n * Get current rate limit status without incrementing\n *\n * @param kv - KV namespace binding\n * @param key - Unique key for rate limit bucket\n * @param maxAttempts - Maximum number of attempts allowed\n * @returns Object with current attempt count and remaining\n */\nexport async function getRateLimitStatus(\n\tkv: KVNamespace,\n\tkey: string,\n\tmaxAttempts: number\n): Promise<{ attempts: number; remaining: number }> {\n\ttry {\n\t\tconst rateLimitKey = `rate_limit:${key}`;\n\t\tconst currentAttemptsStr = await kv.get(rateLimitKey);\n\t\tconst attempts = currentAttemptsStr ? parseInt(currentAttemptsStr, 10) : 0;\n\n\t\treturn {\n\t\t\tattempts,\n\t\t\tremaining: Math.max(0, maxAttempts - attempts),\n\t\t};\n\t} catch (error) {\n\t\tlogger.error('Failed to get rate limit status', {\n\t\t\tkey,\n\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t});\n\n\t\t// Return safe default\n\t\treturn {\n\t\t\tattempts: 0,\n\t\t\tremaining: maxAttempts,\n\t\t};\n\t}\n}\n","import { createMiddleware } from 'hono/factory';\nimport { eq } from 'drizzle-orm';\nimport type { Env, Variables } from '../types';\nimport { getAuthContext } from '../factory';\nimport { problems } from '../lib/problem-json';\n\n/**\n * Middleware that requires the authenticated user to have a verified email.\n * Must be used AFTER requireAuth middleware.\n */\nexport const requireVerifiedEmail = createMiddleware<{ Bindings: Env; Variables: Variables }>(\n\tasync (c, next) => {\n\t\tconst userId = c.get('userId');\n\t\tif (!userId) {\n\t\t\treturn problems.unauthorized(c, 'Not authenticated');\n\t\t}\n\n\t\tconst { db, schema } = getAuthContext(c);\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst database = db as any;\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\tconst users = schema.users as any;\n\t\tconst [user] = await database\n\t\t\t.select({ emailVerified: users.emailVerified })\n\t\t\t.from(users)\n\t\t\t.where(eq(users.id, userId))\n\t\t\t.limit(1);\n\n\t\tif (!user || !user.emailVerified) {\n\t\t\treturn problems.forbidden(c, 'Please verify your email address to continue');\n\t\t}\n\n\t\tawait next();\n\t}\n);\n","// Auth domain router\n// Mounts all auth routes with OpenAPI route definitions\n\nimport { OpenAPIHono } from '@hono/zod-openapi';\nimport type { Env, Variables } from '../types';\nimport { csrf } from '../middleware/csrf';\n// Note: Consumer must apply their own db middleware before mounting auth routes\n\nimport { signupRoute, signupHandler, signupMiddleware } from './signup';\nimport { loginRoute, loginHandler, loginMiddleware } from './login';\nimport { logoutRoute, logoutHandler } from './logout';\nimport { meRoute, meHandler, meMiddleware } from './me';\nimport { verifyEmailRoute, verifyEmailHandler } from './verify-email';\nimport {\n\tforgotPasswordRoute,\n\tforgotPasswordHandler,\n\tforgotPasswordMiddleware,\n} from './forgot-password';\nimport {\n\tresetPasswordRoute,\n\tresetPasswordHandler,\n\tresetPasswordMiddleware,\n} from './reset-password';\nimport {\n\tchangePasswordRoute,\n\tchangePasswordHandler,\n\tchangePasswordMiddleware,\n} from './change-password';\nimport { heartbeatRoute, heartbeatHandler, heartbeatMiddleware } from './heartbeat';\nimport { changeEmailRoute, changeEmailHandler, changeEmailMiddleware } from './change-email';\nimport { confirmEmailChangeRoute, confirmEmailChangeHandler } from './confirm-email-change';\nimport { cancelEmailChangeRoute, cancelEmailChangeHandler } from './cancel-email-change';\nimport {\n\tdeleteAccountRoute,\n\tdeleteAccountHandler,\n\tdeleteAccountMiddleware,\n} from './delete-account';\nimport { refreshRoute, refreshHandler, refreshMiddleware } from './refresh';\nimport {\n\tresendVerificationRoute,\n\tresendVerificationHandler,\n\tresendVerificationMiddleware,\n} from './resend-verification';\nimport twoFa from './2fa';\n\nconst auth = new OpenAPIHono<{ Bindings: Env; Variables: Variables }>();\n\n// Apply domain-level middleware\n// Note: db middleware should be applied by consumer before mounting\nauth.use('*', csrf);\n\n// Mount routes with OpenAPI definitions\n// Note: Middleware is applied in the route path\nauth.use('/signup', ...signupMiddleware);\nauth.openapi(signupRoute, signupHandler);\n\nauth.use('/login', ...loginMiddleware);\nauth.openapi(loginRoute, loginHandler);\n\nauth.openapi(logoutRoute, logoutHandler);\n\nauth.use('/me', ...meMiddleware);\nauth.openapi(meRoute, meHandler);\n\nauth.openapi(verifyEmailRoute, verifyEmailHandler);\n\nauth.use('/forgot-password', ...forgotPasswordMiddleware);\nauth.openapi(forgotPasswordRoute, forgotPasswordHandler);\n\nauth.use('/reset-password', ...resetPasswordMiddleware);\nauth.openapi(resetPasswordRoute, resetPasswordHandler);\n\nauth.use('/change-password', ...changePasswordMiddleware);\nauth.openapi(changePasswordRoute, changePasswordHandler);\n\nauth.use('/heartbeat', ...heartbeatMiddleware);\nauth.openapi(heartbeatRoute, heartbeatHandler);\n\nauth.use('/change-email', ...changeEmailMiddleware);\nauth.openapi(changeEmailRoute, changeEmailHandler);\n\nauth.openapi(confirmEmailChangeRoute, confirmEmailChangeHandler);\n\nauth.openapi(cancelEmailChangeRoute, cancelEmailChangeHandler);\n\nauth.use('/account', ...deleteAccountMiddleware);\nauth.openapi(deleteAccountRoute, deleteAccountHandler);\n\nauth.use('/refresh', ...refreshMiddleware);\nauth.openapi(refreshRoute, refreshHandler);\n\nauth.use('/resend-verification', ...resendVerificationMiddleware);\nauth.openapi(resendVerificationRoute, resendVerificationHandler);\n\n// Mount 2FA subrouter\nauth.route('/2fa', twoFa);\n\nexport default auth;\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { rateLimit } from '../middleware/rateLimit';\nimport { logSecurityEvent } from '../lib/logger';\nimport { getAuthContext } from '../factory';\nimport { hashPassword, validatePasswordWithBreachCheck } from '../core/password';\nimport { generateSecureToken, hashToken } from '../core/tokens';\nimport { EmailService } from '../lib/email';\nimport { createSession } from '../core/session';\nimport { setSessionCookie } from '../core/cookies';\nimport { generateFingerprint, getClientIp } from '../core/fingerprint';\nimport { verifyTurnstileToken } from '../core/turnstile';\nimport { problems } from '../lib/problem-json';\nimport { signupRequestSchema, signupResponseSchema, errorResponseSchema } from './schemas';\n\nexport const signupRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/signup',\n\ttags: ['Authentication'],\n\tsummary: 'Create a new user account',\n\tdescription: 'Registers a new user with email and password. Sends verification email.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: signupRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'User created successfully',\n\t\t\tcontent: { 'application/json': { schema: signupResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid input',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t409: {\n\t\t\tdescription: 'Email already registered',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const signupHandler = async (c: any) => {\n\tconst { email, password, name, turnstileToken, oauthToken } = c.req.valid('json');\n\tconst { db: database, schema } = getAuthContext(c);\n\tconst env = c.env;\n\n\t// --- OAuth signup completion ---\n\tif (oauthToken) {\n\t\tconst stored = await env.OAUTH_STATES.get(`oauth:signup:${oauthToken}`);\n\t\tif (!stored) {\n\t\t\treturn problems.badRequest(c, 'Invalid or expired OAuth token');\n\t\t}\n\n\t\tconst {\n\t\t\temail: tokenEmail,\n\t\t\tprovider,\n\t\t\tproviderUserId,\n\t\t} = JSON.parse(stored) as {\n\t\t\temail: string;\n\t\t\tprovider: string;\n\t\t\tproviderUserId: string;\n\t\t\tinvitationToken?: string | null;\n\t\t};\n\n\t\tif (email.toLowerCase() !== tokenEmail.toLowerCase()) {\n\t\t\treturn problems.badRequest(c, 'Email does not match OAuth token');\n\t\t}\n\n\t\tawait env.OAUTH_STATES.delete(`oauth:signup:${oauthToken}`);\n\n\t\tconst existingUser = await database.query.users.findFirst({\n\t\t\twhere: eq(schema.users.email, tokenEmail.toLowerCase()),\n\t\t});\n\t\tif (existingUser) {\n\t\t\treturn problems.conflict(c, 'Email already registered');\n\t\t}\n\n\t\tlet hashedPassword: string | null = null;\n\t\tif (password) {\n\t\t\tconst passwordValidation = await validatePasswordWithBreachCheck(password);\n\t\t\tif (!passwordValidation.valid) {\n\t\t\t\treturn problems.badRequest(c, passwordValidation.error || 'Invalid password');\n\t\t\t}\n\t\t\thashedPassword = await hashPassword(password, env.PASSWORD_PEPPER_V1);\n\t\t}\n\n\t\tconst [newUser] = await database\n\t\t\t.insert(schema.users)\n\t\t\t.values({\n\t\t\t\temail: tokenEmail.toLowerCase(),\n\t\t\t\tname: name || null,\n\t\t\t\thashedPassword,\n\t\t\t\tpepperKid: hashedPassword ? 'v1' : null,\n\t\t\t\temailVerified: true, // Provider verified the email\n\t\t\t\tactivatedAt: new Date(),\n\t\t\t})\n\t\t\t.returning();\n\n\t\tawait database\n\t\t\t.insert(schema.oauthAccounts)\n\t\t\t.values({ userId: newUser.id, provider, providerUserId, email: tokenEmail.toLowerCase() })\n\t\t\t.onConflictDoNothing();\n\n\t\tconst fingerprint = await generateFingerprint(c.req.raw);\n\t\tconst ipAddress = getClientIp(c.req.raw);\n\t\tconst sessionId = await createSession(database, { sessions: schema.sessions }, newUser.id, fingerprint, ipAddress);\n\n\t\tlogSecurityEvent('user_signup_oauth', 'low', {\n\t\t\tuserId: newUser.id,\n\t\t\temail: newUser.email,\n\t\t\tprovider,\n\t\t});\n\n\t\tc.header('Set-Cookie', setSessionCookie(sessionId, env), { append: true });\n\t\treturn c.json({\n\t\t\tuser: {\n\t\t\t\tid: newUser.id,\n\t\t\t\temail: newUser.email,\n\t\t\t\tname: newUser.name,\n\t\t\t\temailVerified: true,\n\t\t\t},\n\t\t\tredirect: '/',\n\t\t});\n\t}\n\n\t// --- Standard email/password signup ---\n\tif (!password || !turnstileToken) {\n\t\treturn problems.badRequest(c, 'Password and captcha are required');\n\t}\n\n\tconst turnstileValid = await verifyTurnstileToken(\n\t\tturnstileToken,\n\t\tenv.TURNSTILE_SECRET_KEY,\n\t\tc.req.header('cf-connecting-ip') || '',\n\t\tenv.ENVIRONMENT\n\t);\n\tif (!turnstileValid) {\n\t\treturn problems.badRequest(c, 'Invalid captcha');\n\t}\n\n\tconst existingUser = await database.query.users.findFirst({\n\t\twhere: eq(schema.users.email, email.toLowerCase()),\n\t});\n\tif (existingUser) {\n\t\treturn problems.conflict(c, 'Email already registered');\n\t}\n\n\tconst passwordValidation = await validatePasswordWithBreachCheck(password);\n\tif (!passwordValidation.valid) {\n\t\treturn problems.badRequest(c, passwordValidation.error || 'Invalid password');\n\t}\n\n\tconst hashedPassword = await hashPassword(password, env.PASSWORD_PEPPER_V1);\n\n\tconst [newUser] = await database\n\t\t.insert(schema.users)\n\t\t.values({\n\t\t\temail: email.toLowerCase(),\n\t\t\tname: name || null,\n\t\t\thashedPassword,\n\t\t\tpepperKid: 'v1',\n\t\t\temailVerified: false,\n\t\t\tactivatedAt: new Date(),\n\t\t})\n\t\t.returning();\n\n\tconst emailToken = generateSecureToken(32);\n\tconst tokenHash = await hashToken(emailToken);\n\tconst expiresAt = Date.now() + 24 * 60 * 60 * 1000;\n\n\tawait database.insert(schema.emailVerificationTokens).values({\n\t\ttokenHash,\n\t\tuserId: newUser.id,\n\t\temail: newUser.email,\n\t\texpiresAt,\n\t\tcreatedAt: Date.now(),\n\t});\n\n\tconst emailService = new EmailService(env);\n\tconst verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;\n\tawait emailService.sendVerificationEmail(\n\t\t{\n\t\t\temail: newUser.email,\n\t\t\ttoken: emailToken,\n\t\t\tverificationUrl,\n\t\t\tfirstName: newUser.name || undefined,\n\t\t},\n\t\tdatabase\n\t);\n\n\tconst fingerprint = await generateFingerprint(c.req.raw);\n\tconst ipAddress = getClientIp(c.req.raw);\n\tconst sessionId = await createSession(database, { sessions: schema.sessions }, newUser.id, fingerprint, ipAddress);\n\n\tlogSecurityEvent('user_signup', 'low', { userId: newUser.id, email: newUser.email });\n\n\tc.header('Set-Cookie', setSessionCookie(sessionId, c.env), { append: true });\n\treturn c.json({\n\t\tuser: {\n\t\t\tid: newUser.id,\n\t\t\temail: newUser.email,\n\t\t\tname: newUser.name,\n\t\t\temailVerified: newUser.emailVerified,\n\t\t},\n\t\tredirect: '/',\n\t});\n};\n\nexport const signupMiddleware = [\n\trateLimit({\n\t\tidentifier: async (c) => c.req.header('cf-connecting-ip') || 'unknown',\n\t\taction: 'signup',\n\t\tmaxAttempts: 5,\n\t\twindowMs: 3600000, // 1 hour\n\t}),\n];\n","import { eq } from 'drizzle-orm';\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\nimport type { Env } from '../../types';\nimport type {\n\tEmailType,\n\tSendEmailResult,\n\tVerificationEmailData,\n\tPasswordResetEmailData,\n\tEmailChangeConfirmationData,\n\tEmailChangeNotificationData,\n\tTwoFactorCodeEmailData,\n\tTwoFactorEnabledEmailData,\n\tTwoFactorDisabledEmailData,\n} from './types';\nimport {\n\tgenerateVerificationEmail,\n\tgeneratePasswordResetEmail,\n\tgenerateEmailChangeConfirmation,\n\tgenerateEmailChangeNotification,\n\tgenerate2faCodeEmail,\n\tgenerate2faEnabledEmail,\n\tgenerate2faDisabledEmail,\n} from './templates';\nimport { createEmailAdapter, type EmailAdapter } from './adapters';\nimport logger from '../logger';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype UsersTable = PgTableWithColumns<any>;\n\nexport class EmailService {\n\tprivate adapter: EmailAdapter;\n\tprivate env: Env;\n\tprivate get fromAddress(): string {\n\t\tconst appName = this.env.APP_NAME ?? 'Your App';\n\t\tconst appUrl = this.env.APP_URL ?? '';\n\t\tlet domain = 'example.com';\n\t\ttry {\n\t\t\tdomain = new URL(appUrl).hostname;\n\t\t} catch {\n\t\t\t// use default\n\t\t}\n\t\treturn `${appName} <hello@${domain}>`;\n\t}\n\n\tconstructor(env: Env, adapter?: EmailAdapter) {\n\t\tthis.env = env;\n\t\tthis.adapter = adapter ?? createEmailAdapter(env);\n\t}\n\n\t/**\n\t * Check if email address has bounced or complained\n\t */\n\tprivate async shouldBlockEmail(\n\t\temailAddress: string,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<{ blocked: boolean; reason?: string }> {\n\t\tconst [user] = await db\n\t\t\t.select({\n\t\t\t\temailBounced: usersTable.emailBounced,\n\t\t\t\temailComplained: usersTable.emailComplained,\n\t\t\t})\n\t\t\t.from(usersTable)\n\t\t\t.where(eq(usersTable.email, emailAddress.toLowerCase()))\n\t\t\t.limit(1);\n\n\t\tif (!user) {\n\t\t\treturn { blocked: false };\n\t\t}\n\n\t\tif (user.emailBounced) {\n\t\t\treturn { blocked: true, reason: 'Email address has bounced' };\n\t\t}\n\n\t\tif (user.emailComplained) {\n\t\t\treturn { blocked: true, reason: 'User has marked emails as spam' };\n\t\t}\n\n\t\treturn { blocked: false };\n\t}\n\n\t/**\n\t * Get recipient email based on environment\n\t * - Production & Staging: Use real email (verified domain on Resend)\n\t * - Local/Dev/Test: Use Resend test address to avoid sending real emails\n\t */\n\tgetRecipientEmail(userEmail: string, emailType: EmailType): string {\n\t\tconst environment = this.env.ENVIRONMENT?.toLowerCase();\n\n\t\t// Production and staging: send to real recipients\n\t\tif (environment === 'production' || environment === 'staging') {\n\t\t\treturn userEmail;\n\t\t}\n\n\t\t// Local/Test/Development: use Resend test address\n\t\treturn `delivered+${emailType}@resend.dev`;\n\t}\n\n\t/**\n\t * Send verification email\n\t */\n\tasync sendVerificationEmail(\n\t\tdata: VerificationEmailData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\t// Check if email should be blocked\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.email, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.email,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: blockCheck.reason,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst template = generateVerificationEmail(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.email, 'verification');\n\n\t\t\tlogger.info('Sending verification email', {\n\t\t\t\toriginalEmail: data.email,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('Verification email sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send verification email via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send verification email', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.email,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Send password reset email\n\t */\n\tasync sendPasswordResetEmail(\n\t\tdata: PasswordResetEmailData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\t// Check if email should be blocked\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.email, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.email,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: blockCheck.reason,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst template = generatePasswordResetEmail(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.email, 'password_reset');\n\n\t\t\tlogger.info('Sending password reset email', {\n\t\t\t\toriginalEmail: data.email,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('Password reset email sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send password reset email via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send password reset email', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.email,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Send email change confirmation email (to new email address)\n\t */\n\tasync sendEmailChangeConfirmation(\n\t\tdata: EmailChangeConfirmationData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\t// Check if email should be blocked\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.newEmail, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.newEmail,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: blockCheck.reason,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst template = generateEmailChangeConfirmation(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.newEmail, 'email_change_confirmation');\n\n\t\t\tlogger.info('Sending email change confirmation', {\n\t\t\t\toriginalEmail: data.newEmail,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('Email change confirmation sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send email change confirmation via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send email change confirmation', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.newEmail,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Send email change notification email (to old email address)\n\t */\n\tasync sendEmailChangeNotification(\n\t\tdata: EmailChangeNotificationData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\t// Check if email should be blocked\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.oldEmail, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.oldEmail,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: blockCheck.reason,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst template = generateEmailChangeNotification(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.oldEmail, 'email_change_notification');\n\n\t\t\tlogger.info('Sending email change notification', {\n\t\t\t\toriginalEmail: data.oldEmail,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('Email change notification sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send email change notification via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send email change notification', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.oldEmail,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Send 2FA verification code email\n\t */\n\tasync send2faCodeEmail(\n\t\tdata: TwoFactorCodeEmailData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.email, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.email,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: blockCheck.reason };\n\t\t\t}\n\n\t\t\tconst template = generate2faCodeEmail(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.email, '2fa_code');\n\n\t\t\tlogger.info('Sending 2FA code email', {\n\t\t\t\toriginalEmail: data.email,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('2FA code email sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send 2FA code email via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send 2FA code email', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.email,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Send 2FA enabled confirmation email\n\t */\n\tasync send2faEnabledEmail(\n\t\tdata: TwoFactorEnabledEmailData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.email, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.email,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: blockCheck.reason };\n\t\t\t}\n\n\t\t\tconst template = generate2faEnabledEmail(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.email, '2fa_enabled');\n\n\t\t\tlogger.info('Sending 2FA enabled email', {\n\t\t\t\toriginalEmail: data.email,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tmethod: data.method,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('2FA enabled email sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send 2FA enabled email via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send 2FA enabled email', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.email,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Send 2FA disabled notification email\n\t */\n\tasync send2faDisabledEmail(\n\t\tdata: TwoFactorDisabledEmailData,\n\t\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\t\tusersTable: UsersTable\n\t): Promise<SendEmailResult> {\n\t\ttry {\n\t\t\tconst blockCheck = await this.shouldBlockEmail(data.email, db, usersTable);\n\t\t\tif (blockCheck.blocked) {\n\t\t\t\tlogger.warn('Email blocked due to previous bounce/complaint', {\n\t\t\t\t\temail: data.email,\n\t\t\t\t\treason: blockCheck.reason,\n\t\t\t\t});\n\t\t\t\treturn { success: false, error: blockCheck.reason };\n\t\t\t}\n\n\t\t\tconst template = generate2faDisabledEmail(\n\t\t\t\tdata,\n\t\t\t\tthis.env.APP_NAME ?? 'Your App',\n\t\t\t\tthis.env.APP_URL ?? ''\n\t\t\t);\n\t\t\tconst recipient = this.getRecipientEmail(data.email, '2fa_disabled');\n\n\t\t\tlogger.info('Sending 2FA disabled email', {\n\t\t\t\toriginalEmail: data.email,\n\t\t\t\tactualRecipient: recipient,\n\t\t\t\tenvironment: this.env.ENVIRONMENT,\n\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t});\n\n\t\t\tconst result = await this.adapter.send({\n\t\t\t\tfrom: this.fromAddress,\n\t\t\t\tto: recipient,\n\t\t\t\tsubject: template.subject,\n\t\t\t\thtml: template.html,\n\t\t\t\ttext: template.text,\n\t\t\t\ttags: template.tags,\n\t\t\t});\n\n\t\t\tif (result.success && result.emailId) {\n\t\t\t\tlogger.info('2FA disabled email sent successfully', {\n\t\t\t\t\temailId: result.emailId,\n\t\t\t\t\trecipient,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t} else if (!result.success) {\n\t\t\t\tlogger.error('Failed to send 2FA disabled email via adapter', {\n\t\t\t\t\terror: result.error,\n\t\t\t\t\tprovider: this.adapter.providerName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to send 2FA disabled email', {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\temail: data.email,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown error',\n\t\t\t};\n\t\t}\n\t}\n}\n","/**\n * Security utilities for email templates\n * Prevents XSS attacks via URL injection and HTML escaping\n */\n\n/**\n * Derive allowed domains from the APP_URL environment variable\n */\nfunction getAllowedDomains(appUrl: string): string[] {\n\tconst base = ['localhost', '127.0.0.1'];\n\ttry {\n\t\tconst url = new URL(appUrl);\n\t\treturn [...base, url.hostname];\n\t} catch {\n\t\treturn base;\n\t}\n}\n\n/**\n * Sanitize and validate URLs for use in email templates\n * Prevents XSS attacks via javascript: protocol or malicious domains\n *\n * @param url - The URL to sanitize\n * @param appUrl - The APP_URL environment variable used to derive allowed domains\n * @throws {Error} If URL is invalid or uses disallowed protocol/domain\n * @returns The sanitized URL (safe to use in HTML)\n */\nexport function sanitizeEmailUrl(url: string, appUrl: string = ''): string {\n\t// Parse URL to validate structure\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(url);\n\t} catch {\n\t\tthrow new Error('Invalid URL format');\n\t}\n\n\t// Only allow http: and https: protocols\n\tif (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n\t\tthrow new Error(`Disallowed protocol: ${parsed.protocol}`);\n\t}\n\n\t// Validate domain is in allowed list (derived from APP_URL)\n\tconst allowedDomains = getAllowedDomains(appUrl);\n\tconst hostname = parsed.hostname.toLowerCase();\n\tconst isAllowed = allowedDomains.some((domain) => {\n\t\t// Exact match or subdomain match\n\t\treturn hostname === domain || hostname.endsWith(`.${domain}`);\n\t});\n\n\tif (!isAllowed) {\n\t\tthrow new Error(`Disallowed domain: ${hostname}`);\n\t}\n\n\t// Return the sanitized URL (toString() ensures proper encoding)\n\treturn parsed.toString();\n}\n\n/**\n * Escape HTML special characters in a string\n * Prevents HTML injection when displaying URLs or user content\n *\n * @param text - The text to escape\n * @returns The escaped text (safe to display in HTML)\n */\nexport function escapeHtml(text: string): string {\n\tconst htmlEscapeMap: Record<string, string> = {\n\t\t'&': '&',\n\t\t'<': '<',\n\t\t'>': '>',\n\t\t'\"': '"',\n\t\t\"'\": ''',\n\t};\n\n\treturn text.replace(/[&<>\"']/g, (char) => htmlEscapeMap[char]);\n}\n","/**\n * Email Template Component\n * Provides reusable HTML email templates with configurable branding\n */\n\nimport { sanitizeEmailUrl } from '../lib/email/security';\n\nexport interface EmailTemplateOptions {\n\t/** Email heading text */\n\theading: string;\n\t/** Main body content (can include HTML) */\n\tbody: string;\n\t/** Call-to-action button text */\n\tbuttonText: string;\n\t/** Call-to-action button URL */\n\tbuttonUrl: string;\n\t/** Optional welcome message shown below logo */\n\twelcomeMessage?: string;\n\t/** Optional security warning box (HTML) */\n\tsecurityWarning?: string;\n\t/** Footer note text (can include HTML) */\n\tfooterNote: string;\n\t/** Application base URL for images and assets */\n\tappUrl: string;\n\t/** Marketing site URL for footer links */\n\tmarketingUrl?: string;\n\t/** Application name for branding (default: \"App\") */\n\tappName?: string;\n\t/** Optional button background color (default: #00fe9a) */\n\tbuttonBgColor?: string;\n\t/** Optional button text color (default: #000004) */\n\tbuttonTextColor?: string;\n\t/** Optional button font weight (default: 500) */\n\tbuttonFontWeight?: number;\n}\n\n/**\n * Generate a branded HTML email template\n */\nexport function generateEmailTemplate(options: EmailTemplateOptions): string {\n\tconst {\n\t\theading,\n\t\tbody,\n\t\tbuttonText,\n\t\tbuttonUrl,\n\t\twelcomeMessage,\n\t\tsecurityWarning,\n\t\tfooterNote,\n\t\tappUrl,\n\t\tmarketingUrl = appUrl, // Default to appUrl if no marketing site\n\t\tappName = 'App',\n\t\tbuttonBgColor = '#00fe9a',\n\t\tbuttonTextColor = '#000004',\n\t\tbuttonFontWeight = 500,\n\t} = options;\n\n\t// Sanitize all URLs to prevent XSS attacks\n\t// Only sanitize if URL is provided (some emails don't have a button or app URL)\n\tconst safeButtonUrl = buttonUrl ? sanitizeEmailUrl(buttonUrl, appUrl) : '';\n\tconst safeAppUrl = appUrl ? sanitizeEmailUrl(appUrl, appUrl) : '';\n\tconst safeMarketingUrl = marketingUrl ? sanitizeEmailUrl(marketingUrl, appUrl) : '';\n\n\treturn `\n <!DOCTYPE html>\n <html xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" lang=\"en\">\n <head>\n <title>${heading}</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <!--[if mso]>\n <xml>\n <w:WordDocument xmlns:w=\"urn:schemas-microsoft-com:office:word\">\n <w:DontUseAdvancedTypographyReadingMail/>\n </w:WordDocument>\n <o:OfficeDocumentSettings>\n <o:PixelsPerInch>96</o:PixelsPerInch>\n <o:AllowPNG/>\n </o:OfficeDocumentSettings>\n </xml>\n <![endif]-->\n <!--[if !mso]><!-->\n <link href=\"https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap\" rel=\"stylesheet\" type=\"text/css\">\n <!--<![endif]-->\n <style>\n * {\n box-sizing: border-box;\n }\n body {\n margin: 0;\n padding: 0;\n }\n a[x-apple-data-detectors] {\n color: inherit !important;\n text-decoration: inherit !important;\n }\n #MessageViewBody a {\n color: inherit;\n text-decoration: none;\n }\n p {\n line-height: inherit;\n }\n .desktop_hide,\n .desktop_hide table {\n mso-hide: all;\n display: none;\n max-height: 0px;\n overflow: hidden;\n }\n .image_block img+div {\n display: none;\n }\n sup, sub {\n font-size: 75%;\n line-height: 0;\n }\n @media (max-width:660px) {\n .desktop_hide table.icons-inner {\n display: inline-block !important;\n }\n .icons-inner {\n text-align: center;\n }\n .icons-inner td {\n margin: 0 auto;\n }\n .mobile_hide {\n display: none;\n }\n .row-content {\n width: 100% !important;\n max-width: 100% !important;\n }\n .stack .column {\n width: 100%;\n display: block;\n }\n .mobile_hide {\n min-height: 0;\n max-height: 0;\n max-width: 0;\n overflow: hidden;\n font-size: 0px;\n }\n .desktop_hide,\n .desktop_hide table {\n display: table !important;\n max-height: none !important;\n }\n .pad {\n padding-left: 20px !important;\n padding-right: 20px !important;\n }\n }\n </style>\n <!--[if mso ]>\n <style>\n sup, sub { font-size: 100% !important; }\n sup { mso-text-raise:10% }\n sub { mso-text-raise:-10% }\n </style>\n <![endif]-->\n </head>\n <body class=\"body\" style=\"background-color: #f8f9fa; margin: 0; padding: 0; -webkit-text-size-adjust: none; text-size-adjust: none;\">\n <table class=\"nl-container\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f8f9fa;\">\n <tbody>\n <tr>\n <td>\n <!-- Main Content Row -->\n <table class=\"row row-1\" align=\"center\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tbody>\n <tr>\n <td>\n <table class=\"row-content stack\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #ffffff; color: #000000; max-width: 640px; width: 100%; margin: 0 auto;\">\n <tbody>\n <tr>\n <td class=\"column column-1\" width=\"100%\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top;\">\n\n <!-- Top Spacer -->\n <table class=\"divider_block block-1\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:12px;padding-top:30px;\">\n <div class=\"alignment\" align=\"center\">\n <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" width=\"100%\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tr>\n <td class=\"divider_inner\" style=\"font-size: 1px; line-height: 1px; border-top: 0px solid #BBBBBB;\">\n <span style=\"word-break: break-word;\"> </span>\n </td>\n </tr>\n </table>\n </div>\n </td>\n </tr>\n </table>\n\n <!-- Header Image -->\n <table class=\"image_block block-2\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tr>\n <td class=\"pad\" style=\"padding-left:40px;padding-right:40px;width:100%;\">\n <div class=\"alignment\" align=\"center\">\n <div class=\"fullWidth\" style=\"max-width: 50%;\">\n <img src=\"${safeAppUrl}/email-header-image.png\" style=\"display: block; height: auto; border: 0; width: 100%;\" alt=\"${appName}\" title=\"${appName}\" height=\"auto\">\n </div>\n </div>\n </td>\n </tr>\n </table>\n\n ${\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twelcomeMessage\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `\n <!-- Welcome Message -->\n <table class=\"paragraph_block\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-top:30px;padding-left:40px;padding-right:40px;\">\n <div style=\"color:#1e1b4b;font-family:'Montserrat',Arial,sans-serif;font-size:22px;font-weight:600;line-height:1.4;text-align:center;mso-line-height-alt:31px;\">\n <p style=\"margin: 0; word-break: break-word;\">${welcomeMessage}</p>\n </div>\n </td>\n </tr>\n </table>\n `\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n ${\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theading\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `\n <!-- Heading -->\n <table class=\"paragraph_block block-4\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:10px;padding-left:40px;padding-right:40px;padding-top:10px;\">\n <div style=\"color:#1e1b4b;font-family:'Montserrat',Arial,sans-serif;font-size:30px;font-weight:700;line-height:1.2;text-align:center;mso-line-height-alt:36px;\">\n <p style=\"margin: 0; word-break: break-word;\">${heading}</p>\n </div>\n </td>\n </tr>\n </table>\n `\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n <!-- Body Text -->\n <table class=\"paragraph_block block-5\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:30px;padding-left:40px;padding-right:40px;padding-top:30px;\">\n <div style=\"color:#404040;font-family:'Montserrat',Arial,sans-serif;font-size:15px;line-height:1.6;text-align:left;mso-line-height-alt:24px;\">\n <p style=\"margin: 0; word-break: break-word;\">${body}</p>\n </div>\n </td>\n </tr>\n </table>\n\n ${\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsafeButtonUrl\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `\n <!-- Button -->\n <table class=\"button_block block-6\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tr>\n <td class=\"pad\" style=\"padding-left:10px;padding-right:10px;padding-bottom:10px;text-align:center;\">\n <div class=\"alignment\" align=\"center\">\n <!--[if mso]>\n <v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" href=\"${safeButtonUrl}\" style=\"height:52px;width:auto;v-text-anchor:middle;\" arcsize=\"12%\" fillcolor=\"${buttonBgColor}\">\n <v:stroke dashstyle=\"Solid\" weight=\"0px\" color=\"${buttonBgColor}\"/>\n <w:anchorlock/>\n <v:textbox inset=\"0px,0px,0px,0px\">\n <center dir=\"false\" style=\"color:${buttonTextColor};font-family:'Montserrat',Arial,sans-serif;font-size:16px;font-weight:${buttonFontWeight};\">\n <![endif]-->\n <a href=\"${safeButtonUrl}\" style=\"background-color: ${buttonBgColor}; border: 0px solid transparent; border-radius: 6px; color: ${buttonTextColor}; display: inline-block; font-family: 'Montserrat', Arial, sans-serif; font-size: 16px; font-weight: ${buttonFontWeight}; mso-border-alt: none; padding: 10px 20px; text-align: center; text-decoration: none; text-transform: capitalize; word-break: keep-all;\">\n ${buttonText}\n </a>\n <!--[if mso]>\n </center>\n </v:textbox>\n </v:roundrect>\n <![endif]-->\n </div>\n </td>\n </tr>\n </table>\n\n <!-- Copy Link -->\n <table class=\"paragraph_block\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:30px;padding-left:40px;padding-right:40px;\">\n <div style=\"color:#737373;font-family:'Montserrat',Arial,sans-serif;font-size:12px;line-height:1.5;text-align:center;mso-line-height-alt:18px;\">\n <p style=\"margin: 0 0 5px 0; word-break: break-word;\">or copy and paste this link into your browser:</p>\n <a href=\"${safeButtonUrl}\" style=\"color:#4a90d9;text-decoration:underline;word-break:break-all;\">${safeButtonUrl}</a>\n </div>\n </td>\n </tr>\n </table>\n `\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n ${\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsecurityWarning\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? `\n <!-- Security Warning -->\n <table class=\"paragraph_block\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-left:40px;padding-right:40px;padding-bottom:20px;\">\n <div style=\"background-color: #a1f1ff; border-radius: 8px; padding: 16px; display: flex; align-items: center; justify-content: center;\">\n <p style=\"margin: 0; color: #01001b; font-family: 'Montserrat', Arial, sans-serif; font-size: 13px; line-height: 1.5; text-align: center;\">\n ${securityWarning}\n </p>\n </div>\n </td>\n </tr>\n </table>\n `\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: ''\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n <!-- Footer Note -->\n <table class=\"paragraph_block\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:30px;padding-left:40px;padding-right:40px;\">\n <div style=\"color:#737373;font-family:'Montserrat',Arial,sans-serif;font-size:13px;line-height:1.5;text-align:center;mso-line-height-alt:20px;\">\n <p style=\"margin: 0; word-break: break-word;\">${footerNote}</p>\n </div>\n </td>\n </tr>\n </table>\n\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Spacer Row -->\n <table class=\"row row-2\" align=\"center\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tbody>\n <tr>\n <td>\n <table class=\"row-content stack\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #f8f9fa; color: #000000; max-width: 640px; width: 100%; margin: 0 auto;\">\n <tbody>\n <tr>\n <td class=\"column column-1\" width=\"100%\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; padding-bottom: 5px; vertical-align: top;\">\n <table class=\"empty_block block-1\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tr>\n <td class=\"pad\">\n <div></div>\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Footer Row -->\n <table class=\"row row-3\" align=\"center\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt;\">\n <tbody>\n <tr>\n <td>\n <table class=\"row-content stack\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #1e1b4b; color: #000000; max-width: 640px; width: 100%; margin: 0 auto;\">\n <tbody>\n <tr>\n <td class=\"column column-1\" width=\"100%\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding: 20px;\">\n\n <!-- Policy Links -->\n <table class=\"paragraph_block\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:20px;padding-left:40px;padding-right:40px;padding-top:20px;\">\n <div style=\"color:#ffffff;font-family:'Montserrat',Arial,sans-serif;font-size:13px;line-height:1.5;text-align:center;mso-line-height-alt:20px;\">\n <p style=\"margin: 0; word-break: break-word;\">\n <a href=\"${safeMarketingUrl}/privacy\" style=\"color: #ffffff; text-decoration: underline;\">Privacy Policy</a> | <a href=\"${safeMarketingUrl}/contact\" style=\"color: #ffffff; text-decoration: underline;\">Get in Touch</a>\n </p>\n </div>\n </td>\n </tr>\n </table>\n\n <!-- Copyright & Disclaimer -->\n <table class=\"paragraph_block block-1\" width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;\">\n <tr>\n <td class=\"pad\" style=\"padding-bottom:15px;padding-left:40px;padding-right:40px;\">\n <div style=\"color:#a3a3a3;font-family:'Montserrat',Arial,sans-serif;font-size:12px;line-height:1.5;text-align:center;mso-line-height-alt:18px;\">\n <p style=\"margin: 0 0 12px 0; word-break: break-word;\">© ${new Date().getFullYear()} ${appName}. All rights reserved.</p>\n <p style=\"margin: 0; font-size: 11px; line-height: 1.5; word-break: break-word;\">\n Please do not reply to this email as this address does not accept incoming messages. This service email contains essential information relating to your ${appName} account.\n </p>\n </div>\n </td>\n </tr>\n </table>\n\n </td>\n </tr>\n </tbody>\n </table>\n </td>\n </tr>\n </tbody>\n </table>\n\n </td>\n </tr>\n </tbody>\n </table>\n </body>\n </html>\n `;\n}\n","import type {\n\tEmailTemplate,\n\tVerificationEmailData,\n\tPasswordResetEmailData,\n\tEmailChangeConfirmationData,\n\tEmailChangeNotificationData,\n\tTwoFactorCodeEmailData,\n\tTwoFactorEnabledEmailData,\n\tTwoFactorDisabledEmailData,\n} from './types';\nimport { sanitizeEmailUrl, escapeHtml } from './security';\nimport { generateEmailTemplate } from '../../core/email-template';\n\n/**\n * Extract base URL from a full URL (e.g., https://flow.example.com/verify?token=xxx → https://flow.example.com)\n */\nexport function extractAppUrl(url: string): string {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn `${parsed.protocol}//${parsed.host}`;\n\t} catch {\n\t\t// Fallback to a generic URL if parsing fails\n\t\treturn 'https://your-domain.com';\n\t}\n}\n\n/**\n * Generate email verification email\n */\nexport function generateVerificationEmail(\n\tdata: VerificationEmailData,\n\tappName: string = 'Your App',\n\tconfiguredAppUrl: string = ''\n): EmailTemplate {\n\tconst { email, verificationUrl, firstName } = data;\n\n\t// Sanitize URL to prevent XSS attacks — only allow domains derived from APP_URL env var\n\tconst appUrl = configuredAppUrl;\n\tconst safeUrl = sanitizeEmailUrl(verificationUrl, appUrl);\n\tconst safeFirstName = firstName ? escapeHtml(firstName) : null;\n\n\tconst html = generateEmailTemplate({\n\t\theading: '',\n\t\tbody: `Please verify that your email address is <strong>${escapeHtml(email)}</strong>, and that you entered it when signing up for ${escapeHtml(appName)}.`,\n\t\tbuttonText: 'Verify Email',\n\t\tbuttonUrl: safeUrl,\n\t\tbuttonBgColor: '#00fe9a',\n\t\tbuttonTextColor: '#000004',\n\t\tbuttonFontWeight: 500,\n\t\twelcomeMessage: safeFirstName\n\t\t\t? `Hi ${safeFirstName}, Welcome to ${escapeHtml(appName)}!`\n\t\t\t: undefined,\n\t\tfooterNote: '',\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: email,\n\t\tsubject: `Verify Your Email - ${appName}`,\n\t\thtml,\n\t\ttext: `Verify Your Email Address\\n\\nThank you for signing up for ${appName}! Please verify your email address by clicking the link below:\\n\\n${safeUrl}\\n\\nThis link expires in 48 hours.\\n\\nIf you didn't create an account with ${appName}, you can safely ignore this email.`,\n\t\ttags: {\n\t\t\ttype: 'verification',\n\t\t},\n\t};\n}\n\n/**\n * Generate password reset email\n */\nexport function generatePasswordResetEmail(\n\tdata: PasswordResetEmailData,\n\tappName: string = 'Your App',\n\tconfiguredAppUrl: string = ''\n): EmailTemplate {\n\tconst { email, resetUrl } = data;\n\n\t// Sanitize URL to prevent XSS attacks — only allow domains derived from APP_URL env var\n\tconst appUrl = configuredAppUrl;\n\tconst safeUrl = sanitizeEmailUrl(resetUrl, appUrl);\n\n\tconst html = generateEmailTemplate({\n\t\theading: 'Reset Your Password',\n\t\tbody: 'We received a request to reset your password. Click the button below to create a new password.',\n\t\tbuttonText: 'Reset Password',\n\t\tbuttonUrl: safeUrl,\n\t\tbuttonBgColor: '#00fe9a',\n\t\tbuttonTextColor: '#000004',\n\t\tbuttonFontWeight: 500,\n\t\tsecurityWarning: `\n\t\t\t<strong>Link expires in 1 hour.</strong><br>\n\t\t\tFor security, this link can only be used once.\n\t\t`,\n\t\tfooterNote:\n\t\t\t\"If you didn't request this change, no action is needed. Your password will remain unchanged.\",\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: email,\n\t\tsubject: `Reset Your Password - ${appName}`,\n\t\thtml,\n\t\ttext: `Reset Your Password\\n\\nWe received a request to reset your password. Click the link below to create a new password:\\n\\n${safeUrl}\\n\\nThis link will expire in 1 hour.\\n\\nIf you didn't request a password reset, you can safely ignore this email.`,\n\t\ttags: {\n\t\t\ttype: 'password_reset',\n\t\t},\n\t};\n}\n\n/**\n * Generate email change confirmation email (sent to NEW email)\n */\nexport function generateEmailChangeConfirmation(\n\tdata: EmailChangeConfirmationData,\n\tappName: string = 'Your App',\n\tconfiguredAppUrl: string = ''\n): EmailTemplate {\n\tconst { newEmail, confirmUrl } = data;\n\n\tconst appUrl = configuredAppUrl;\n\tconst safeUrl = sanitizeEmailUrl(confirmUrl, appUrl);\n\n\tconst html = generateEmailTemplate({\n\t\theading: 'Confirm Your New Email',\n\t\tbody: `We received a request to change your ${escapeHtml(appName)} account email to this address. Click the button below to confirm this change.`,\n\t\tbuttonText: 'Confirm Email Change',\n\t\tbuttonUrl: safeUrl,\n\t\tbuttonBgColor: '#00fe9a',\n\t\tbuttonTextColor: '#000004',\n\t\tbuttonFontWeight: 500,\n\t\tsecurityWarning: `\n\t\t\t<strong>This link expires in 24 hours.</strong><br>\n\t\t\tFor security, this link can only be used once.\n\t\t`,\n\t\tfooterNote: \"If you didn't request this change, you can safely ignore this email.\",\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: newEmail,\n\t\tsubject: `Confirm Your New Email Address - ${appName}`,\n\t\thtml,\n\t\ttext: `Confirm Your New Email Address\\n\\nWe received a request to change your ${appName} account email to this address.\\n\\nClick the link below to confirm this change:\\n\\n${safeUrl}\\n\\nThis link expires in 24 hours.\\n\\nIf you didn't request this change, you can safely ignore this email.`,\n\t\ttags: {\n\t\t\ttype: 'email_change_confirmation',\n\t\t},\n\t};\n}\n\n/**\n * Generate email change notification email (sent to OLD email)\n */\nexport function generateEmailChangeNotification(\n\tdata: EmailChangeNotificationData,\n\tappName: string = 'Your App',\n\tconfiguredAppUrl: string = ''\n): EmailTemplate {\n\tconst { oldEmail, newEmail, cancelUrl } = data;\n\n\tconst appUrl = configuredAppUrl;\n\tconst safeUrl = sanitizeEmailUrl(cancelUrl, appUrl);\n\tconst safeNewEmail = escapeHtml(newEmail);\n\n\tconst html = generateEmailTemplate({\n\t\theading: 'Email Change Requested',\n\t\tbody: `Someone requested to change your ${escapeHtml(appName)} account email to <strong>${safeNewEmail}</strong>.`,\n\t\tbuttonText: \"This Wasn't Me - Cancel Change\",\n\t\tbuttonUrl: safeUrl,\n\t\tbuttonBgColor: '#00fe9a',\n\t\tbuttonTextColor: '#000004',\n\t\tbuttonFontWeight: 500,\n\t\tsecurityWarning: `\n\t\t\tIf you made this request, no action is needed.<br>\n\t\t\tComplete the change by clicking the link sent to your new email address.\n\t\t`,\n\t\tfooterNote: \"If you didn't request this change, click the button above to cancel it.\",\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: oldEmail,\n\t\tsubject: `Email Change Requested - ${appName}`,\n\t\thtml,\n\t\ttext: `Email Change Requested\\n\\nSomeone requested to change your ${appName} account email to ${newEmail}.\\n\\nIf you made this request, no action is needed. Complete the change by clicking the link sent to your new email address.\\n\\nIf you didn't request this change, click the link below to cancel it:\\n\\n${safeUrl}`,\n\t\ttags: {\n\t\t\ttype: 'email_change_notification',\n\t\t},\n\t};\n}\n\n/**\n * Generate 2FA verification code email\n */\nexport function generate2faCodeEmail(\n\tdata: TwoFactorCodeEmailData,\n\tappName: string = 'Your App',\n\tappUrl: string = ''\n): EmailTemplate {\n\tconst { email, firstName, code } = data;\n\tconst safeFirstName = firstName ? escapeHtml(firstName) : 'there';\n\n\tconst html = generateEmailTemplate({\n\t\theading: `Your ${escapeHtml(appName)} verification code`,\n\t\tbody: `\n\t\t\t<p>Hi ${safeFirstName},</p>\n\t\t\t<p>Your verification code is:</p>\n\t\t\t<p style=\"font-size: 32px; font-weight: bold; letter-spacing: 4px; text-align: center; margin: 24px 0; font-family: monospace;\">${escapeHtml(code)}</p>\n\t\t\t<p>This code expires in 5 minutes.</p>\n\t\t\t<p>If you didn't request this code, you can safely ignore this email.</p>\n\t\t`,\n\t\tbuttonText: '',\n\t\tbuttonUrl: '',\n\t\tfooterNote: '',\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: email,\n\t\tsubject: `Your ${appName} verification code`,\n\t\thtml,\n\t\ttext: `Hi ${firstName || 'there'},\\n\\nYour verification code is:\\n\\n${code}\\n\\nThis code expires in 5 minutes.\\n\\nIf you didn't request this code, you can safely ignore this email.\\n\\n— ${appName}`,\n\t\ttags: { type: '2fa_code' },\n\t};\n}\n\n/**\n * Generate 2FA enabled confirmation email\n */\nexport function generate2faEnabledEmail(\n\tdata: TwoFactorEnabledEmailData,\n\tappName: string = 'Your App',\n\tappUrl: string = ''\n): EmailTemplate {\n\tconst { email, firstName, method } = data;\n\tconst safeFirstName = firstName ? escapeHtml(firstName) : 'there';\n\tconst methodName = method === 'totp' ? 'an authenticator app' : 'email codes';\n\n\tconst html = generateEmailTemplate({\n\t\theading: 'Two-factor authentication enabled',\n\t\tbody: `\n\t\t\t<p>Hi ${safeFirstName},</p>\n\t\t\t<p>Two-factor authentication has been enabled on your ${escapeHtml(appName)} account using ${methodName}.</p>\n\t\t\t<p>If you didn't make this change, please contact support immediately and reset your password.</p>\n\t\t`,\n\t\tbuttonText: '',\n\t\tbuttonUrl: '',\n\t\tfooterNote: '',\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: email,\n\t\tsubject: 'Two-factor authentication enabled',\n\t\thtml,\n\t\ttext: `Hi ${firstName || 'there'},\\n\\nTwo-factor authentication has been enabled on your ${appName} account using ${methodName}.\\n\\nIf you didn't make this change, please contact support immediately and reset your password.\\n\\n— ${appName}`,\n\t\ttags: { type: '2fa_enabled' },\n\t};\n}\n\n/**\n * Generate 2FA disabled notification email\n */\nexport function generate2faDisabledEmail(\n\tdata: TwoFactorDisabledEmailData,\n\tappName: string = 'Your App',\n\tappUrl: string = ''\n): EmailTemplate {\n\tconst { email, firstName } = data;\n\tconst safeFirstName = firstName ? escapeHtml(firstName) : 'there';\n\n\tconst html = generateEmailTemplate({\n\t\theading: 'Two-factor authentication disabled',\n\t\tbody: `\n\t\t\t<p>Hi ${safeFirstName},</p>\n\t\t\t<p>Two-factor authentication has been disabled on your ${escapeHtml(appName)} account.</p>\n\t\t\t<p>If you didn't make this change, your account may be compromised. Please reset your password immediately and re-enable 2FA.</p>\n\t\t`,\n\t\tbuttonText: 'Reset Password',\n\t\tbuttonUrl: appUrl ? `${appUrl}/forgot-password` : '',\n\t\tbuttonBgColor: '#ef4444',\n\t\tbuttonTextColor: '#ffffff',\n\t\tfooterNote: '',\n\t\tappUrl,\n\t});\n\n\treturn {\n\t\tto: email,\n\t\tsubject: 'Two-factor authentication disabled',\n\t\thtml,\n\t\ttext: `Hi ${firstName || 'there'},\\n\\nTwo-factor authentication has been disabled on your ${appName} account.\\n\\nIf you didn't make this change, your account may be compromised. Please reset your password immediately and re-enable 2FA.\\n\\n— ${appName}`,\n\t\ttags: { type: '2fa_disabled' },\n\t};\n}\n","import { Resend } from 'resend';\nimport type { EmailAdapter, EmailSendOptions, EmailSendResult } from './types';\nimport logger from '../../logger';\n\n/**\n * Resend email adapter\n * Wraps the Resend SDK for sending transactional emails\n */\nexport class ResendAdapter implements EmailAdapter {\n\tprivate client: Resend;\n\treadonly providerName = 'resend';\n\n\tconstructor(apiKey: string) {\n\t\tif (!apiKey || apiKey.trim() === '') {\n\t\t\tthrow new Error(\n\t\t\t\t'RESEND_API_KEY is required - sign up at https://resend.com for free API key'\n\t\t\t);\n\t\t}\n\n\t\tif (apiKey.startsWith('re_TODO')) {\n\t\t\tlogger.warn('Using placeholder Resend API key - emails will not be sent');\n\t\t}\n\n\t\tthis.client = new Resend(apiKey);\n\t}\n\n\tasync send(options: EmailSendOptions): Promise<EmailSendResult> {\n\t\ttry {\n\t\t\tlogger.info('Resend: Sending email', {\n\t\t\t\tprovider: this.providerName,\n\t\t\t\tto: options.to,\n\t\t\t\tsubject: options.subject,\n\t\t\t});\n\n\t\t\tconst result = await this.client.emails.send({\n\t\t\t\tfrom: options.from,\n\t\t\t\tto: options.to,\n\t\t\t\tsubject: options.subject,\n\t\t\t\thtml: options.html,\n\t\t\t\ttext: options.text,\n\t\t\t\ttags: options.tags\n\t\t\t\t\t? Object.entries(options.tags).map(([name, value]) => ({ name, value }))\n\t\t\t\t\t: undefined,\n\t\t\t});\n\n\t\t\tif (result.data?.id) {\n\t\t\t\tlogger.info('Resend: Email sent successfully', {\n\t\t\t\t\tprovider: this.providerName,\n\t\t\t\t\temailId: result.data.id,\n\t\t\t\t\tto: options.to,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\temailId: result.data.id,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Handle error response from Resend\n\t\t\tif (result.error) {\n\t\t\t\tconst errorMessage = result.error.message || 'Unknown Resend error';\n\t\t\t\tlogger.error('Resend API error', {\n\t\t\t\t\tprovider: this.providerName,\n\t\t\t\t\terror: errorMessage,\n\t\t\t\t\tto: options.to,\n\t\t\t\t\tsubject: options.subject,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: errorMessage,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tlogger.error('Resend: Unexpected API response', {\n\t\t\t\tprovider: this.providerName,\n\t\t\t\tresponse: JSON.stringify(result),\n\t\t\t\tto: options.to,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: 'Unexpected API response from email provider',\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : 'Unknown error';\n\t\t\tlogger.error('Resend send error', {\n\t\t\t\tprovider: this.providerName,\n\t\t\t\terror: errorMessage,\n\t\t\t\tto: options.to,\n\t\t\t\tsubject: options.subject,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: errorMessage,\n\t\t\t};\n\t\t}\n\t}\n}\n","import type { Env } from '../../../types';\nimport type { EmailAdapter } from './types';\nimport { ResendAdapter } from './resend-adapter';\nimport logger from '../../logger';\n\n/**\n * Create the Resend email adapter\n *\n * @param env - Environment variables\n * @returns ResendAdapter instance\n * @throws Error if RESEND_API_KEY is missing\n */\nexport function createEmailAdapter(env: Env): EmailAdapter {\n\tlogger.info('Creating email adapter', { provider: 'resend' });\n\n\tif (!env.RESEND_API_KEY) {\n\t\tthrow new Error('RESEND_API_KEY is required');\n\t}\n\treturn new ResendAdapter(env.RESEND_API_KEY);\n}\n","import { Resend } from 'resend';\nimport logger from '../logger';\n\n/**\n * Create Resend client with API key validation\n *\n * IMPORTANT: You need a valid Resend API key for all environments (local, staging, production).\n * - Local/Test: Uses delivered@resend.dev test addresses (no real emails sent)\n * - Staging: Uses delivered+staging@resend.dev\n * - Production: Uses real user email addresses\n *\n * Sign up at https://resend.com for a free API key (3,000 emails/month free tier).\n */\nexport function createResendClient(apiKey: string): Resend {\n\tif (!apiKey || apiKey.trim() === '') {\n\t\tthrow new Error('RESEND_API_KEY is required - sign up at https://resend.com for free API key');\n\t}\n\n\tif (!apiKey.startsWith('re_')) {\n\t\tlogger.warn('Using placeholder Resend API key - emails will not be sent');\n\t}\n\n\treturn new Resend(apiKey);\n}\n","import { eq } from 'drizzle-orm';\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\nimport type { ResendWebhookPayload } from './types';\nimport logger, { logError } from '../logger';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype EmailEventsTable = PgTableWithColumns<any>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype UsersTable = PgTableWithColumns<any>;\n\n/**\n * Handle Resend webhook event\n * Processes email delivery events and updates database accordingly\n */\nexport async function handleWebhookEvent(\n\tpayload: ResendWebhookPayload,\n\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\ttables: { emailEvents: EmailEventsTable; users: UsersTable }\n): Promise<void> {\n\tconst { type, data } = payload;\n\tconst emailAddress = data.to[0]; // Primary recipient\n\n\tlogger.info('Processing Resend webhook event', {\n\t\teventType: type,\n\t\temailId: data.email_id,\n\t\temailAddress,\n\t});\n\n\t// Insert event record (idempotent via unique constraint)\n\ttry {\n\t\tawait db.insert(tables.emailEvents).values({\n\t\t\temailId: data.email_id,\n\t\t\teventType: type,\n\t\t\temailAddress,\n\t\t\tmetadata: payload as unknown as Record<string, unknown>,\n\t\t});\n\t} catch (error) {\n\t\t// Ignore duplicate key errors (idempotency)\n\t\tif (\n\t\t\terror instanceof Error &&\n\t\t\terror.message.includes('unique constraint') &&\n\t\t\terror.message.includes('email_events_email_id_event_type_idx')\n\t\t) {\n\t\t\tlogger.info('Webhook event already processed (idempotent)', {\n\t\t\t\teventType: type,\n\t\t\t\temailId: data.email_id,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthrow error;\n\t}\n\n\t// Handle critical events\n\tswitch (type) {\n\t\tcase 'email.bounced':\n\t\t\tawait handleBounce(emailAddress, data.email_id, data.bounce, db, tables.users);\n\t\t\tbreak;\n\n\t\tcase 'email.complained':\n\t\t\tawait handleComplaint(emailAddress, data.email_id, db, tables.users);\n\t\t\tbreak;\n\n\t\tcase 'email.failed':\n\t\t\tawait handleFailure(emailAddress, data.email_id, data.failed);\n\t\t\tbreak;\n\n\t\tcase 'email.delivery_delayed':\n\t\t\tlogger.warn('Email delivery delayed', {\n\t\t\t\temailId: data.email_id,\n\t\t\t\temailAddress,\n\t\t\t});\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t// Log other events (sent, delivered, opened, clicked) for monitoring\n\t\t\tlogger.info('Resend webhook event processed', {\n\t\t\t\teventType: type,\n\t\t\t\temailId: data.email_id,\n\t\t\t});\n\t}\n}\n\n/**\n * Handle email bounce (permanent rejection)\n */\nasync function handleBounce(\n\temailAddress: string,\n\temailId: string,\n\tbounce: { message: string; subType: string; type: 'Permanent' | 'Transient' } | undefined,\n\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\tusersTable: UsersTable\n): Promise<void> {\n\tlogger.warn('Email bounced - marking user email as invalid', {\n\t\temailAddress,\n\t\temailId,\n\t\tbounceType: bounce?.type,\n\t\tbounceMessage: bounce?.message,\n\t});\n\n\t// Mark user as bounced (prevent future sends)\n\tawait db\n\t\t.update(usersTable)\n\t\t.set({\n\t\t\temailBounced: true,\n\t\t\temailBouncedAt: new Date(),\n\t\t})\n\t\t.where(eq(usersTable.email, emailAddress.toLowerCase()));\n\n\tlogError(new Error('Email bounced'), {\n\t\tcontext: 'email_webhook',\n\t\temailAddress,\n\t\temailId,\n\t\tbounceType: bounce?.type,\n\t});\n}\n\n/**\n * Handle spam complaint\n */\nasync function handleComplaint(\n\temailAddress: string,\n\temailId: string,\n\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\tusersTable: UsersTable\n): Promise<void> {\n\tlogger.warn('Email marked as spam - unsubscribing user', {\n\t\temailAddress,\n\t\temailId,\n\t});\n\n\t// Mark user as complained (CAN-SPAM compliance)\n\tawait db\n\t\t.update(usersTable)\n\t\t.set({\n\t\t\temailComplained: true,\n\t\t\temailComplainedAt: new Date(),\n\t\t})\n\t\t.where(eq(usersTable.email, emailAddress.toLowerCase()));\n\n\tlogError(new Error('Email spam complaint received'), {\n\t\tcontext: 'email_webhook',\n\t\temailAddress,\n\t\temailId,\n\t});\n}\n\n/**\n * Handle send failure\n */\nasync function handleFailure(\n\temailAddress: string,\n\temailId: string,\n\tfailed: { reason: string } | undefined\n): Promise<void> {\n\tlogger.error('Email failed to send', {\n\t\temailAddress,\n\t\temailId,\n\t\treason: failed?.reason,\n\t});\n\n\tlogError(new Error('Email send failure'), {\n\t\tcontext: 'email_webhook',\n\t\temailAddress,\n\t\temailId,\n\t\treason: failed?.reason,\n\t});\n}\n","import { sql } from 'drizzle-orm';\nimport type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\nimport logger from '../logger';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype EmailEventsTable = PgTableWithColumns<any>;\n\n/**\n * Calculate bounce rate for monitoring\n * Returns percentage of emails that bounced out of total sent\n */\nexport async function calculateBounceRate(\n\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\temailEventsTable: EmailEventsTable,\n\thoursAgo = 24\n): Promise<number> {\n\tconst cutoff = new Date(Date.now() - hoursAgo * 60 * 60 * 1000);\n\n\tconst [stats] = await db\n\t\t.select({\n\t\t\ttotalSent: sql<number>`count(*) filter (where event_type = 'email.sent')`.as('total_sent'),\n\t\t\ttotalBounced: sql<number>`count(*) filter (where event_type = 'email.bounced')`.as(\n\t\t\t\t'total_bounced'\n\t\t\t),\n\t\t})\n\t\t.from(emailEventsTable)\n\t\t.where(sql`created_at > ${cutoff}`);\n\n\tif (!stats || stats.totalSent === 0) {\n\t\treturn 0;\n\t}\n\n\tconst bounceRate = (stats.totalBounced / stats.totalSent) * 100;\n\n\t// Alert if bounce rate > 5% (industry standard threshold)\n\tif (bounceRate > 5) {\n\t\tlogger.error('High bounce rate detected', {\n\t\t\tbounceRate: `${bounceRate.toFixed(2)}%`,\n\t\t\ttotalSent: stats.totalSent,\n\t\t\ttotalBounced: stats.totalBounced,\n\t\t\thoursAgo,\n\t\t\taction: 'Review email list quality and sending practices',\n\t\t});\n\t}\n\n\treturn bounceRate;\n}\n\n/**\n * Calculate complaint rate for monitoring\n * Returns percentage of emails marked as spam out of total sent\n */\nexport async function calculateComplaintRate(\n\tdb: PostgresJsDatabase<Record<string, unknown>>,\n\temailEventsTable: EmailEventsTable,\n\thoursAgo = 24\n): Promise<number> {\n\tconst cutoff = new Date(Date.now() - hoursAgo * 60 * 60 * 1000);\n\n\tconst [stats] = await db\n\t\t.select({\n\t\t\ttotalSent: sql<number>`count(*) filter (where event_type = 'email.sent')`.as('total_sent'),\n\t\t\ttotalComplained: sql<number>`count(*) filter (where event_type = 'email.complained')`.as(\n\t\t\t\t'total_complained'\n\t\t\t),\n\t\t})\n\t\t.from(emailEventsTable)\n\t\t.where(sql`created_at > ${cutoff}`);\n\n\tif (!stats || stats.totalSent === 0) {\n\t\treturn 0;\n\t}\n\n\tconst complaintRate = (stats.totalComplained / stats.totalSent) * 100;\n\n\t// Alert if complaint rate > 0.1% (damages sender reputation)\n\tif (complaintRate > 0.1) {\n\t\tlogger.error('High complaint rate detected', {\n\t\t\tcomplaintRate: `${complaintRate.toFixed(4)}%`,\n\t\t\ttotalSent: stats.totalSent,\n\t\t\ttotalComplained: stats.totalComplained,\n\t\t\thoursAgo,\n\t\t\taction: 'Review email content and unsubscribe process immediately',\n\t\t});\n\t}\n\n\treturn complaintRate;\n}\n","// Auth domain Zod schemas for OpenAPI documentation\n// All auth route request/response schemas with .openapi() decorators for automatic spec generation\n\nimport { z } from '@hono/zod-openapi';\n\n// ============================================\n// Request Schemas\n// ============================================\n\n/**\n * POST /v1/auth/signup\n * Create a new user account\n */\nexport const signupRequestSchema = z\n\t.object({\n\t\temail: z.string().email().openapi({ example: 'user@example.com' }),\n\t\t// Password and turnstileToken are required for standard signup; optional when oauthToken is provided\n\t\tpassword: z.string().min(8).optional().openapi({ example: 'SecurePass123!' }),\n\t\tname: z.string().min(1).optional().openapi({ example: 'John Doe' }),\n\t\tturnstileToken: z.string().optional().openapi({ example: 'XXXX.DUMMY.TOKEN.XXXX' }),\n\t\t// OAuth signup completion token (from /signup?oauth_token=...)\n\t\toauthToken: z.string().optional().openapi({ example: 'a1b2c3d4-...' }),\n\t})\n\t.openapi('SignupRequest');\n\n/**\n * POST /v1/auth/login\n * Authenticate with email and password\n */\nexport const loginRequestSchema = z\n\t.object({\n\t\temail: z.string().email().openapi({ example: 'user@example.com' }),\n\t\tpassword: z.string().min(1).openapi({ example: 'SecurePass123!' }),\n\t\tturnstileToken: z.string().min(1).openapi({ example: 'XXXX.DUMMY.TOKEN.XXXX' }),\n\t})\n\t.openapi('LoginRequest');\n\n/**\n * POST /v1/auth/verify-email\n * Verify email address with token\n */\nexport const verifyEmailRequestSchema = z\n\t.object({\n\t\ttoken: z.string().min(1).openapi({ example: 'verification-token-abc123' }),\n\t})\n\t.openapi('VerifyEmailRequest');\n\n/**\n * POST /v1/auth/forgot-password\n * Request password reset email\n */\nexport const forgotPasswordRequestSchema = z\n\t.object({\n\t\temail: z.string().email().openapi({ example: 'user@example.com' }),\n\t\tturnstileToken: z.string().min(1).openapi({ example: 'XXXX.DUMMY.TOKEN.XXXX' }),\n\t})\n\t.openapi('ForgotPasswordRequest');\n\n/**\n * POST /v1/auth/reset-password\n * Reset password with token\n */\nexport const resetPasswordRequestSchema = z\n\t.object({\n\t\ttoken: z.string().min(1).openapi({ example: 'reset-token-xyz789' }),\n\t\tpassword: z.string().min(8).openapi({ example: 'NewSecurePass456!' }),\n\t\tturnstileToken: z.string().min(1).openapi({ example: 'XXXX.DUMMY.TOKEN.XXXX' }),\n\t})\n\t.openapi('ResetPasswordRequest');\n\n/**\n * POST /v1/auth/change-password\n * Change password for authenticated user\n */\nexport const changePasswordRequestSchema = z\n\t.object({\n\t\tcurrentPassword: z.string().min(1).openapi({ example: 'OldPassword123!' }),\n\t\tnewPassword: z.string().min(8).openapi({ example: 'NewPassword456!' }),\n\t})\n\t.openapi('ChangePasswordRequest');\n\n// ============================================\n// Response Schemas\n// ============================================\n\n/**\n * User object returned in auth responses\n */\nexport const userSchema = z\n\t.object({\n\t\tid: z.string().uuid().openapi({ example: 'a1b2c3d4-e5f6-47a8-9b0c-1d2e3f4a5b6c' }),\n\t\temail: z.string().email().openapi({ example: 'user@example.com' }),\n\t\tname: z.string().nullable().openapi({ example: 'John Doe' }),\n\t\temailVerified: z.boolean().openapi({ example: true }),\n\t\tavatarUrl: z\n\t\t\t.string()\n\t\t\t.url()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.openapi({ example: 'https://example.com/avatar.jpg' }),\n\t\ttheme: z.enum(['light', 'dark', 'system']).nullable().optional().openapi({ example: 'dark' }),\n\t\ttimezone: z.string().nullable().optional().openapi({ example: 'America/New_York' }),\n\t\tcreatedAt: z.string().datetime().optional().openapi({ example: '2024-01-15T10:30:00Z' }),\n\t})\n\t.openapi('User');\n\n/**\n * POST /v1/auth/signup response\n */\nexport const signupResponseSchema = z\n\t.object({\n\t\tuser: userSchema,\n\t\tredirect: z.string().openapi({ example: '/verify-email' }),\n\t})\n\t.openapi('SignupResponse');\n\n/**\n * POST /v1/auth/login response\n * Can return user + redirect OR require 2FA challenge\n */\nexport const loginResponseSchema = z\n\t.object({\n\t\tuser: userSchema.optional(),\n\t\tredirect: z.string().optional().openapi({ example: '/dashboard' }),\n\t\trequires2fa: z.boolean().optional().openapi({ example: true }),\n\t\tmethods: z\n\t\t\t.array(z.enum(['totp', 'email']))\n\t\t\t.optional()\n\t\t\t.openapi({ example: ['totp', 'email'] }),\n\t})\n\t.openapi('LoginResponse');\n\n/**\n * POST /v1/auth/logout response\n */\nexport const logoutResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('LogoutResponse');\n\n/**\n * GET /v1/auth/me response\n */\nexport const meResponseSchema = z\n\t.object({\n\t\tuser: userSchema,\n\t\ttwoFactorEnabled: z.boolean().openapi({ example: false }),\n\t\ttwoFactorMethods: z.array(z.enum(['totp', 'email'])).openapi({ example: [] }),\n\t\temailBounced: z.boolean().openapi({ example: false }),\n\t})\n\t.openapi('MeResponse');\n\n/**\n * POST /v1/auth/verify-email response\n */\nexport const verifyEmailResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tredirect: z.string().openapi({ example: '/dashboard' }),\n\t})\n\t.openapi('VerifyEmailResponse');\n\n/**\n * POST /v1/auth/forgot-password response\n */\nexport const forgotPasswordResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('ForgotPasswordResponse');\n\n/**\n * POST /v1/auth/reset-password response\n */\nexport const resetPasswordResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tredirect: z.string().openapi({ example: '/login' }),\n\t})\n\t.openapi('ResetPasswordResponse');\n\n/**\n * POST /v1/auth/change-password response\n */\nexport const changePasswordResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('ChangePasswordResponse');\n\n/**\n * GET /v1/auth/heartbeat response\n */\nexport const heartbeatResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\ttimestamp: z\n\t\t\t.number()\n\t\t\t.openapi({ example: 1705315800000, description: 'Unix timestamp in milliseconds' }),\n\t})\n\t.openapi('HeartbeatResponse');\n\n// ============================================\n// Error Schema (RFC 9457 Problem Details)\n// ============================================\n\n/**\n * RFC 9457 Problem Details for HTTP APIs\n * Standard error response format for all auth endpoints\n */\nexport const errorResponseSchema = z\n\t.object({\n\t\ttype: z.string().url().openapi({\n\t\t\texample: 'https://api.example.com/errors/unauthorized',\n\t\t\tdescription: 'URI identifying the problem type',\n\t\t}),\n\t\ttitle: z.string().openapi({\n\t\t\texample: 'Unauthorized',\n\t\t\tdescription: 'Short, human-readable summary of the problem type',\n\t\t}),\n\t\tstatus: z.number().int().min(400).max(599).openapi({\n\t\t\texample: 401,\n\t\t\tdescription: 'HTTP status code',\n\t\t}),\n\t\tdetail: z.string().optional().openapi({\n\t\t\texample: 'Invalid credentials provided',\n\t\t\tdescription: 'Human-readable explanation specific to this occurrence',\n\t\t}),\n\t\tinstance: z.string().optional().openapi({\n\t\t\texample: 'POST /v1/auth/login',\n\t\t\tdescription: 'URI identifying the specific occurrence of the problem',\n\t\t}),\n\t\ttraceId: z.string().optional().openapi({\n\t\t\texample: 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',\n\t\t\tdescription: 'OpenTelemetry trace ID for distributed tracing',\n\t\t}),\n\t\trequestId: z.string().optional().openapi({\n\t\t\texample: 'req_abc123xyz789',\n\t\t\tdescription: 'Request ID from X-Request-ID header for correlation',\n\t\t}),\n\t})\n\t.openapi('ErrorResponse');\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { rateLimit } from '../middleware/rateLimit';\nimport { logSecurityEvent } from '../lib/logger';\nimport { getAuthContext } from '../factory';\nimport { verifyPasswordWithRotation } from '../core/password';\nimport { generateSecureToken } from '../core/tokens';\nimport { createSession } from '../core/session';\nimport { setSessionCookie, setChallengeCookie } from '../core/cookies';\nimport { generateFingerprint, getClientIp } from '../core/fingerprint';\nimport { verifyTurnstileToken } from '../core/turnstile';\nimport { storeChallengeToken, validateTrustedDevice } from '../core/2fa';\nimport { getTrustedDeviceCookieName } from '../core/cookies';\nimport { hashToken } from '../core/tokens';\nimport { getCookie } from 'hono/cookie';\nimport {\n\tcheckAccountLocked,\n\tincrementFailedAttempts,\n\tclearAccountLockout,\n} from '../core/account-lockout';\nimport { problems } from '../lib/problem-json';\nimport { loginRequestSchema, loginResponseSchema, errorResponseSchema } from './schemas';\nimport { getUserEnabled2faMethods } from './2fa/helpers';\nimport { AUTH_DEFAULTS } from '../core/config';\n\nexport const loginRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/login',\n\ttags: ['Authentication'],\n\tsummary: 'Authenticate user',\n\tdescription: 'Login with email and password. May require 2FA verification if enabled.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: loginRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Login successful or 2FA required',\n\t\t\tcontent: { 'application/json': { schema: loginResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid input',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Invalid credentials',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t429: {\n\t\t\tdescription: 'Account locked due to too many failed attempts',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const loginHandler = async (c: any) => {\n\tconst { email, password, turnstileToken } = c.req.valid('json');\n\tconst { db: database, schema } = getAuthContext(c);\n\tconst env = c.env;\n\n\t// Verify Turnstile token\n\tconst turnstileValid = await verifyTurnstileToken(\n\t\tturnstileToken,\n\t\tenv.TURNSTILE_SECRET_KEY,\n\t\tc.req.header('cf-connecting-ip') || '',\n\t\tenv.ENVIRONMENT\n\t);\n\tif (!turnstileValid) {\n\t\treturn problems.badRequest(c, 'Invalid captcha');\n\t}\n\n\t// Find user\n\tconst user = await database.query.users.findFirst({\n\t\twhere: eq(schema.users.email, email.toLowerCase()),\n\t});\n\n\tif (!user) {\n\t\treturn problems.unauthorized(c, 'Invalid email or password');\n\t}\n\n\t// Check account lockout\n\tconst lockStatus = await checkAccountLocked(database, { users: schema.users }, user.id);\n\tif (lockStatus.isLocked) {\n\t\tconst unlockSeconds = lockStatus.unlockAt\n\t\t\t? Math.ceil((lockStatus.unlockAt - Date.now()) / 1000)\n\t\t\t: 15 * 60; // 15 minutes in seconds\n\t\treturn problems.rateLimitExceeded(c, unlockSeconds);\n\t}\n\n\t// Verify password\n\tif (!user.hashedPassword) {\n\t\treturn problems.unauthorized(c, 'Invalid email or password');\n\t}\n\n\tconst pepper =\n\t\tuser.pepperKid === 'v1'\n\t\t\t? env.PASSWORD_PEPPER_V1\n\t\t\t: env.PASSWORD_PEPPER_V2 || env.PASSWORD_PEPPER_V1;\n\tconst passwordResult = await verifyPasswordWithRotation(\n\t\tpassword,\n\t\tuser.hashedPassword,\n\t\tpepper,\n\t\tenv.PASSWORD_PEPPER_V2\n\t);\n\n\tif (!passwordResult.verified) {\n\t\tawait incrementFailedAttempts(database, { users: schema.users }, user.id);\n\t\tlogSecurityEvent('login_failed', 'medium', { userId: user.id, email: user.email });\n\t\treturn problems.unauthorized(c, 'Invalid email or password');\n\t}\n\n\t// Clear any lockout on successful login\n\tawait clearAccountLockout(database, { users: schema.users }, user.id);\n\n\t// Check if 2FA is enabled\n\tconst enabled2faMethods = await getUserEnabled2faMethods(database, user.id, schema.user2faMethods);\n\tif (enabled2faMethods.length > 0) {\n\t\t// Check for trusted device cookie - skip 2FA if valid\n\t\tconst trustedDeviceCookieName = getTrustedDeviceCookieName(env);\n\t\tconst trustedDeviceToken = getCookie(c, trustedDeviceCookieName);\n\n\t\tif (trustedDeviceToken) {\n\t\t\tconst tokenHash = await hashToken(trustedDeviceToken);\n\t\t\tconst isValidDevice = await validateTrustedDevice(\n\t\t\t\tdatabase,\n\t\t\t\tschema.userTrustedDevices,\n\t\t\t\tuser.id,\n\t\t\t\ttokenHash\n\t\t\t);\n\n\t\t\tif (isValidDevice) {\n\t\t\t\t// Trusted device - skip 2FA and create session\n\t\t\t\tlogSecurityEvent('login_trusted_device', 'low', { userId: user.id });\n\t\t\t\tconst fingerprint = await generateFingerprint(c.req.raw);\n\t\t\t\tconst ipAddress = getClientIp(c.req.raw);\n\t\t\t\tconst sessionId = await createSession(\n\t\t\t\t\tdatabase,\n\t\t\t\t\t{ sessions: schema.sessions },\n\t\t\t\t\tuser.id,\n\t\t\t\t\tfingerprint,\n\t\t\t\t\tipAddress\n\t\t\t\t);\n\n\t\t\t\tlogSecurityEvent('login_success', 'low', { userId: user.id });\n\n\t\t\t\tc.header('Set-Cookie', setSessionCookie(sessionId, env), { append: true });\n\t\t\t\treturn c.json({\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\temailVerified: user.emailVerified,\n\t\t\t\t\t},\n\t\t\t\t\tredirect: '/',\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// 2FA required - create challenge token\n\t\tconst challengeToken = generateSecureToken(32);\n\t\tconst challengePayload = {\n\t\t\tuserId: user.id,\n\t\t\tmethods: enabled2faMethods.map((m) => m.method) as ('totp' | 'email')[],\n\t\t\tinvitationToken: null,\n\t\t\texpiresAt: Date.now() + AUTH_DEFAULTS.TWO_FACTOR_CHALLENGE_TTL_SECONDS * 1000,\n\t\t\tattempts: 0,\n\t\t\tcreatedAt: Date.now(),\n\t\t};\n\n\t\tawait storeChallengeToken(env.OAUTH_STATES, challengeToken, challengePayload);\n\t\tsetChallengeCookie(c, env, challengeToken);\n\n\t\treturn c.json({\n\t\t\trequires2fa: true,\n\t\t\tmethods: enabled2faMethods.map((m) => m.method),\n\t\t});\n\t}\n\n\t// Create session\n\tconst fingerprint = await generateFingerprint(c.req.raw);\n\tconst ipAddress = getClientIp(c.req.raw);\n\tconst sessionId = await createSession(database, { sessions: schema.sessions }, user.id, fingerprint, ipAddress);\n\n\tlogSecurityEvent('login_success', 'low', { userId: user.id });\n\n\tc.header('Set-Cookie', setSessionCookie(sessionId, c.env), { append: true });\n\treturn c.json({\n\t\tuser: {\n\t\t\tid: user.id,\n\t\t\temail: user.email,\n\t\t\tname: user.name,\n\t\t\temailVerified: user.emailVerified,\n\t\t},\n\t\tredirect: '/',\n\t});\n};\n\nexport const loginMiddleware = [\n\trateLimit({\n\t\tidentifier: async (c) => c.req.header('cf-connecting-ip') || 'unknown',\n\t\taction: 'login',\n\t\tmaxAttempts: 10,\n\t\twindowMs: 900000, // 15 minutes\n\t}),\n];\n","// Shared 2FA helper functions\n// Extracted from 2fa.ts to reduce duplication\n\nimport { eq, and, isNull } from 'drizzle-orm';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleTable = any;\nimport type { Env } from '../../types';\nimport { hashToken } from '../../core/tokens';\nimport { verifyTotpCode, decryptTotpSecret, verifyBackupCode } from '../../core/2fa';\nimport { logSecurityEvent } from '../../lib/logger';\n\n/**\n * Get user's enabled 2FA methods (reusable query)\n */\nexport async function getUserEnabled2faMethods(\n\tdatabase: DrizzleDB,\n\tuserId: string,\n\tuser2faMethodsTable: DrizzleTable\n): Promise<\n\tArray<{\n\t\tmethod: 'totp' | 'email';\n\t\tisPrimary: boolean;\n\t\ttotpSecret: string | null;\n\t\tlastTotpCounter: number | null;\n\t}>\n> {\n\treturn database\n\t\t.select({\n\t\t\tmethod: user2faMethodsTable.method,\n\t\t\tisPrimary: user2faMethodsTable.isPrimary,\n\t\t\ttotpSecret: user2faMethodsTable.totpSecret,\n\t\t\tlastTotpCounter: user2faMethodsTable.lastTotpCounter,\n\t\t})\n\t\t.from(user2faMethodsTable)\n\t\t.where(eq(user2faMethodsTable.userId, userId));\n}\n\n/**\n * Generate 6-digit email OTP\n */\nexport function generateEmailOtp(): string {\n\tconst bytes = crypto.getRandomValues(new Uint8Array(4));\n\tconst num = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0;\n\treturn String(num % 1000000).padStart(6, '0');\n}\n\n/**\n * Verify any 2FA code (TOTP, email, or backup)\n */\nexport async function verifyAny2faCode(\n\tdatabase: DrizzleDB,\n\tenv: Env,\n\tuserId: string,\n\tcode: string,\n\tmethod: 'totp' | 'email' | 'backup',\n\ttables: { user2faMethods: DrizzleTable; userBackupCodes: DrizzleTable }\n): Promise<{ valid: boolean }> {\n\tconst { user2faMethods: user2faMethodsTable, userBackupCodes: userBackupCodesTable } = tables;\n\n\tif (method === 'totp') {\n\t\tconst lockKey = `totp_verify_lock:${userId}`;\n\t\tconst existingLock = await env.OAUTH_STATES.get(lockKey);\n\n\t\tif (existingLock) {\n\t\t\tlogSecurityEvent('2fa_totp_concurrent_attempt', 'medium', { userId });\n\t\t\treturn { valid: false };\n\t\t}\n\n\t\tawait env.OAUTH_STATES.put(lockKey, Date.now().toString(), { expirationTtl: 60 });\n\n\t\ttry {\n\t\t\tconst methods = await getUserEnabled2faMethods(database, userId, user2faMethodsTable);\n\t\t\tconst totpMethod = methods.find((m) => m.method === 'totp');\n\t\t\tif (!totpMethod?.totpSecret) return { valid: false };\n\n\t\t\tconst secret = await decryptTotpSecret(totpMethod.totpSecret, env.TOTP_ENCRYPTION_KEY);\n\t\t\tconst result = verifyTotpCode(secret, code, totpMethod.lastTotpCounter);\n\n\t\t\tif (result.valid) {\n\t\t\t\tawait database\n\t\t\t\t\t.update(user2faMethodsTable)\n\t\t\t\t\t.set({ lastTotpCounter: result.counter })\n\t\t\t\t\t.where(and(eq(user2faMethodsTable.userId, userId), eq(user2faMethodsTable.method, 'totp')));\n\n\t\t\t\treturn { valid: true };\n\t\t\t}\n\t\t\treturn { valid: false };\n\t\t} finally {\n\t\t\tawait env.OAUTH_STATES.delete(lockKey);\n\t\t}\n\t}\n\n\tif (method === 'email') {\n\t\tconst kvKey = `email_2fa_challenge:${userId}`;\n\t\tconst storedHash = await env.OAUTH_STATES.get(kvKey);\n\t\tif (!storedHash) return { valid: false };\n\n\t\tconst codeHash = await hashToken(code);\n\t\tif (codeHash === storedHash) {\n\t\t\tawait env.OAUTH_STATES.delete(kvKey);\n\t\t\treturn { valid: true };\n\t\t}\n\t\treturn { valid: false };\n\t}\n\n\tif (method === 'backup') {\n\t\tconst backupCodes = await database\n\t\t\t.select({ id: userBackupCodesTable.id, codeHash: userBackupCodesTable.codeHash })\n\t\t\t.from(userBackupCodesTable)\n\t\t\t.where(and(eq(userBackupCodesTable.userId, userId), isNull(userBackupCodesTable.usedAt)));\n\n\t\tfor (const bc of backupCodes) {\n\t\t\tif (await verifyBackupCode(code, bc.codeHash)) {\n\t\t\t\tawait database\n\t\t\t\t\t.update(userBackupCodesTable)\n\t\t\t\t\t.set({ usedAt: Date.now() })\n\t\t\t\t\t.where(eq(userBackupCodesTable.id, bc.id));\n\n\t\t\t\tlogSecurityEvent('2fa_backup_code_used', 'medium', { userId, codeId: bc.id });\n\t\t\t\treturn { valid: true };\n\t\t\t}\n\t\t}\n\t\treturn { valid: false };\n\t}\n\n\treturn { valid: false };\n}\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { getAuthContext } from '../factory';\nimport { clearSessionCookie, getSessionCookieName } from '../core/cookies';\nimport { logoutResponseSchema } from './schemas';\n\nexport const logoutRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/logout',\n\ttags: ['Authentication'],\n\tsummary: 'End user session',\n\tdescription: 'Logs out the current user and clears session cookies.',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Logout successful',\n\t\t\tcontent: { 'application/json': { schema: logoutResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const logoutHandler = async (c: any) => {\n\tconst { db: database, schema } = getAuthContext(c);\n\tconst cookieHeader = c.req.header('cookie');\n\n\tif (cookieHeader) {\n\t\tconst cookieName = getSessionCookieName(c.env);\n\t\tconst cookiePattern = new RegExp(`${cookieName}=([^;]+)`);\n\t\tconst sessionIdMatch = cookieHeader.match(cookiePattern);\n\n\t\tif (sessionIdMatch) {\n\t\t\tconst sessionId = sessionIdMatch[1];\n\t\t\tawait database.delete(schema.sessions).where(eq(schema.sessions.id, sessionId));\n\t\t}\n\t}\n\n\tc.header('Set-Cookie', clearSessionCookie(c.env), { append: true });\n\treturn c.json({ success: true });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { requireAuth } from '../middleware/auth';\nimport { getAuthContext } from '../factory';\nimport { problems } from '../lib/problem-json';\nimport { meResponseSchema, errorResponseSchema } from './schemas';\nimport { getUserEnabled2faMethods } from './2fa/helpers';\n\nexport const meRoute = createRoute({\n\tmethod: 'get',\n\tpath: '/me',\n\ttags: ['Authentication'],\n\tsummary: 'Get current user profile',\n\tdescription: \"Returns the authenticated user's profile information and 2FA status.\",\n\tsecurity: [{ cookieAuth: [] }],\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'User profile retrieved successfully',\n\t\t\tcontent: { 'application/json': { schema: meResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Not authenticated',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'User not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const meHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c, 'Not authenticated');\n\n\tconst { db: database, schema } = getAuthContext(c);\n\n\tconst user = await database.query.users.findFirst({\n\t\twhere: eq(schema.users.id, userId),\n\t});\n\n\tif (!user) {\n\t\treturn problems.notFound(c, 'User not found');\n\t}\n\n\tconst enabled2faMethods = await getUserEnabled2faMethods(database, userId, schema.user2faMethods);\n\n\treturn c.json({\n\t\tuser: {\n\t\t\tid: user.id,\n\t\t\temail: user.email,\n\t\t\tname: user.name,\n\t\t\temailVerified: user.emailVerified,\n\t\t\tavatarUrl: user.avatarUrl,\n\t\t\ttheme: user.theme,\n\t\t\ttimezone: user.timezone,\n\t\t\tcreatedAt: user.createdAt,\n\t\t},\n\t\ttwoFactorEnabled: enabled2faMethods.length > 0,\n\t\ttwoFactorMethods: enabled2faMethods.map((m) => m.method),\n\t\temailBounced: user.emailBounced || user.emailComplained,\n\t});\n};\n\nexport const meMiddleware = [requireAuth];\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { logSecurityEvent } from '../lib/logger';\nimport { getAuthContext } from '../factory';\nimport { hashToken } from '../core/tokens';\nimport { problems } from '../lib/problem-json';\nimport {\n\tverifyEmailRequestSchema,\n\tverifyEmailResponseSchema,\n\terrorResponseSchema,\n} from './schemas';\n\nexport const verifyEmailRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/verify-email',\n\ttags: ['Authentication'],\n\tsummary: 'Verify email address',\n\tdescription: \"Verifies a user's email address using the token sent via email.\",\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: verifyEmailRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Email verified successfully',\n\t\t\tcontent: { 'application/json': { schema: verifyEmailResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid or expired token',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const verifyEmailHandler = async (c: any) => {\n\tconst { token } = c.req.valid('json');\n\tconst { db: database, schema } = getAuthContext(c);\n\n\tconst tokenHash = await hashToken(token);\n\n\tconst verificationToken = await database.query.emailVerificationTokens.findFirst({\n\t\twhere: eq(schema.emailVerificationTokens.tokenHash, tokenHash),\n\t});\n\n\tif (!verificationToken) {\n\t\treturn problems.badRequest(c, 'Invalid or expired verification token');\n\t}\n\n\tif (verificationToken.expiresAt < Date.now()) {\n\t\tawait database\n\t\t\t.delete(schema.emailVerificationTokens)\n\t\t\t.where(eq(schema.emailVerificationTokens.tokenHash, tokenHash));\n\t\treturn problems.badRequest(c, 'Verification token has expired');\n\t}\n\n\t// Update user email verified status\n\tawait database\n\t\t.update(schema.users)\n\t\t.set({ emailVerified: true })\n\t\t.where(eq(schema.users.id, verificationToken.userId));\n\n\t// Delete the used token\n\tawait database\n\t\t.delete(schema.emailVerificationTokens)\n\t\t.where(eq(schema.emailVerificationTokens.tokenHash, tokenHash));\n\n\tlogSecurityEvent('email_verified', 'low', { userId: verificationToken.userId });\n\n\treturn c.json({ success: true, redirect: '/' });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { rateLimit } from '../middleware/rateLimit';\nimport { logSecurityEvent } from '../lib/logger';\nimport { createPasswordResetToken } from '../core/password-reset';\nimport { EmailService } from '../lib/email';\nimport { verifyTurnstileToken } from '../core/turnstile';\nimport { problems } from '../lib/problem-json';\nimport {\n\tforgotPasswordRequestSchema,\n\tforgotPasswordResponseSchema,\n\terrorResponseSchema,\n} from './schemas';\n\nexport const forgotPasswordRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/forgot-password',\n\ttags: ['Authentication'],\n\tsummary: 'Request password reset',\n\tdescription:\n\t\t'Sends a password reset email to the specified address if it exists. Always returns success to prevent email enumeration.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: forgotPasswordRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Password reset email sent (if account exists)',\n\t\t\tcontent: { 'application/json': { schema: forgotPasswordResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid captcha',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const forgotPasswordHandler = async (c: any) => {\n\tconst { email, turnstileToken } = c.req.valid('json');\n\tconst database = c.get('db');\n\tconst env = c.env;\n\n\t// Verify Turnstile token\n\tconst turnstileValid = await verifyTurnstileToken(\n\t\tturnstileToken,\n\t\tenv.TURNSTILE_SECRET_KEY,\n\t\tc.req.header('cf-connecting-ip') || '',\n\t\tenv.ENVIRONMENT\n\t);\n\tif (!turnstileValid) {\n\t\treturn problems.badRequest(c, 'Invalid captcha');\n\t}\n\n\t// Create password reset token (returns null if user not found or not verified)\n\tconst resetResult = await createPasswordResetToken(database, email);\n\n\t// Always return success to prevent email enumeration\n\tif (!resetResult) {\n\t\treturn c.json({ success: true });\n\t}\n\n\t// Send password reset email\n\tconst emailService = new EmailService(env);\n\tconst resetUrl = `${env.APP_URL}/reset-password?token=${resetResult.token}`;\n\tawait emailService.sendPasswordResetEmail(\n\t\t{\n\t\t\temail: email.toLowerCase(),\n\t\t\ttoken: resetResult.token,\n\t\t\tresetUrl,\n\t\t},\n\t\tdatabase\n\t);\n\n\tlogSecurityEvent('password_reset_requested', 'medium', { userId: resetResult.userId });\n\n\treturn c.json({ success: true });\n};\n\nexport const forgotPasswordMiddleware = [\n\trateLimit({\n\t\tidentifier: async (c) => c.req.header('cf-connecting-ip') || 'unknown',\n\t\taction: 'forgot_password',\n\t\tmaxAttempts: 5,\n\t\twindowMs: 3600000, // 1 hour\n\t}),\n];\n","// Password reset utilities\n// Implements secure password reset flow with time-limited tokens\n\nimport { eq, and, gt, lt, sql } from 'drizzle-orm';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\nimport { generateSecureToken, hashToken } from './tokens';\nimport { hashPassword } from './password';\nimport logger from '../lib/logger';\nimport { AUTH_DEFAULTS } from './config';\n\nconst TOKEN_EXPIRATION_MS = AUTH_DEFAULTS.PASSWORD_RESET_TTL_MINUTES * 60 * 1000;\n\n// Table type definitions for dependency injection\ntype UsersTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\temail: any;\n\t\temailVerified: any;\n\t\thashedPassword: any;\n\t\tpepperKid: any;\n\t\tsessionVersion: any;\n\t};\n\tdialect: 'pg';\n}>;\n\ntype PasswordResetTokensTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\ttokenHash: any;\n\t\tuserId: any;\n\t\texpiresAt: any;\n\t\tused: any;\n\t\tcreatedAt: any;\n\t};\n\tdialect: 'pg';\n}>;\n\ntype SessionsTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\tuserId: any;\n\t};\n\tdialect: 'pg';\n}>;\n\nexport interface PasswordResetTables {\n\tusers: UsersTable;\n\tpasswordResetTokens: PasswordResetTokensTable;\n\tsessions: SessionsTable;\n}\n\n/**\n * Create a password reset token for a user\n * @param db - Drizzle database instance\n * @param tables - Password reset tables (users, passwordResetTokens)\n * @param email - User email address\n * @returns Token string (to be sent in email) or null if user not found\n */\nexport async function createPasswordResetToken(\n\tdb: DrizzleDB,\n\ttables: Pick<PasswordResetTables, 'users' | 'passwordResetTokens'>,\n\temail: string\n): Promise<{ token: string; userId: string } | null> {\n\tconst { users, passwordResetTokens } = tables;\n\n\t// Find user by email\n\tconst [user] = await db\n\t\t.select({ id: users.id, emailVerified: users.emailVerified })\n\t\t.from(users)\n\t\t.where(eq(users.email, email.toLowerCase()))\n\t\t.limit(1);\n\n\tif (!user) {\n\t\treturn null;\n\t}\n\n\t// Only allow password reset for verified accounts\n\tif (!user.emailVerified) {\n\t\treturn null;\n\t}\n\n\t// Delete any existing reset tokens for this user\n\tawait db.delete(passwordResetTokens).where(eq(passwordResetTokens.userId, user.id));\n\n\t// Generate new token\n\tconst token = generateSecureToken(64);\n\tconst tokenHash = await hashToken(token);\n\tconst expiresAt = Date.now() + TOKEN_EXPIRATION_MS;\n\n\t// Store hashed token\n\tawait db.insert(passwordResetTokens).values({\n\t\ttokenHash,\n\t\tuserId: user.id,\n\t\texpiresAt,\n\t\tused: false,\n\t\tcreatedAt: Date.now(),\n\t});\n\n\tlogger.info('Password reset token created', { userId: user.id });\n\n\treturn { token, userId: user.id };\n}\n\n/**\n * Validate a password reset token\n * @param db - Drizzle database instance\n * @param tables - Password reset tables (passwordResetTokens)\n * @param token - Token from reset link\n * @returns User ID if valid, null otherwise\n */\nexport async function validateResetToken(\n\tdb: DrizzleDB,\n\ttables: Pick<PasswordResetTables, 'passwordResetTokens'>,\n\ttoken: string\n): Promise<string | null> {\n\tconst { passwordResetTokens } = tables;\n\tconst tokenHash = await hashToken(token);\n\n\tconst [resetToken] = await db\n\t\t.select({\n\t\t\tuserId: passwordResetTokens.userId,\n\t\t\tused: passwordResetTokens.used,\n\t\t\texpiresAt: passwordResetTokens.expiresAt,\n\t\t})\n\t\t.from(passwordResetTokens)\n\t\t.where(\n\t\t\tand(\n\t\t\t\teq(passwordResetTokens.tokenHash, tokenHash),\n\t\t\t\teq(passwordResetTokens.used, false),\n\t\t\t\tgt(passwordResetTokens.expiresAt, Date.now())\n\t\t\t)\n\t\t)\n\t\t.limit(1);\n\n\tif (!resetToken) {\n\t\treturn null;\n\t}\n\n\treturn resetToken.userId;\n}\n\n/**\n * Reset user password using a valid token\n * Invalidates all existing sessions for security\n * @param db - Drizzle database instance\n * @param tables - Password reset tables (users, passwordResetTokens, sessions)\n * @param token - Password reset token\n * @param newPassword - New password (already validated)\n * @param pepper - Pepper used for hashing\n * @param pepperKid - Which pepper version was used (default: 'v1')\n * @returns Success status\n */\nexport async function resetPassword(\n\tdb: DrizzleDB,\n\ttables: PasswordResetTables,\n\ttoken: string,\n\tnewPassword: string,\n\tpepper: string,\n\tpepperKid: 'v1' | 'v2' = 'v1'\n): Promise<{ success: boolean; error?: string }> {\n\tconst { users, passwordResetTokens, sessions } = tables;\n\tconst userId = await validateResetToken(db, { passwordResetTokens }, token);\n\n\tif (!userId) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: 'This password reset link is invalid or has expired.',\n\t\t};\n\t}\n\n\t// Hash new password\n\tconst hashedPassword = await hashPassword(newPassword, pepper);\n\n\t// Update user password and increment session version\n\tawait db\n\t\t.update(users)\n\t\t.set({\n\t\t\thashedPassword,\n\t\t\tpepperKid,\n\t\t\tsessionVersion: sql`${users.sessionVersion} + 1`,\n\t\t})\n\t\t.where(eq(users.id, userId));\n\n\t// Mark token as used\n\tconst tokenHash = await hashToken(token);\n\tawait db\n\t\t.update(passwordResetTokens)\n\t\t.set({ used: true })\n\t\t.where(eq(passwordResetTokens.tokenHash, tokenHash));\n\n\t// Invalidate all existing sessions (force re-login)\n\tawait db.delete(sessions).where(eq(sessions.userId, userId));\n\n\tlogger.info('Password reset successful', { userId });\n\n\treturn { success: true };\n}\n\n/**\n * Clean up expired password reset tokens\n * Should be called periodically (e.g., via cron)\n * @param db - Drizzle database instance\n * @param tables - Password reset tables (passwordResetTokens)\n */\nexport async function cleanupExpiredResetTokens(\n\tdb: DrizzleDB,\n\ttables: Pick<PasswordResetTables, 'passwordResetTokens'>\n): Promise<void> {\n\tconst { passwordResetTokens } = tables;\n\tconst cutoffTime = Date.now();\n\tawait db.delete(passwordResetTokens).where(lt(passwordResetTokens.expiresAt, cutoffTime));\n}\n","import { createRoute } from '@hono/zod-openapi';\nimport { rateLimit } from '../middleware/rateLimit';\nimport { logSecurityEvent } from '../lib/logger';\nimport { validatePassword } from '../core/password';\nimport { resetPassword } from '../core/password-reset';\nimport { verifyTurnstileToken } from '../core/turnstile';\nimport { problems } from '../lib/problem-json';\nimport {\n\tresetPasswordRequestSchema,\n\tresetPasswordResponseSchema,\n\terrorResponseSchema,\n} from './schemas';\n\nexport const resetPasswordRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/reset-password',\n\ttags: ['Authentication'],\n\tsummary: 'Reset password with token',\n\tdescription: \"Resets a user's password using a valid reset token from the forgot-password flow.\",\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: resetPasswordRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Password reset successfully',\n\t\t\tcontent: { 'application/json': { schema: resetPasswordResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid input or expired token',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const resetPasswordHandler = async (c: any) => {\n\tconst { token, password, turnstileToken } = c.req.valid('json');\n\tconst database = c.get('db');\n\tconst env = c.env;\n\n\t// Verify Turnstile token\n\tconst turnstileValid = await verifyTurnstileToken(\n\t\tturnstileToken,\n\t\tenv.TURNSTILE_SECRET_KEY,\n\t\tc.req.header('cf-connecting-ip') || '',\n\t\tenv.ENVIRONMENT\n\t);\n\tif (!turnstileValid) {\n\t\treturn problems.badRequest(c, 'Invalid captcha');\n\t}\n\n\t// Validate password strength\n\tconst passwordValidation = validatePassword(password);\n\tif (!passwordValidation.valid) {\n\t\treturn problems.badRequest(c, passwordValidation.error || 'Invalid password');\n\t}\n\n\t// Reset password\n\tconst result = await resetPassword(database, token, password, env.PASSWORD_PEPPER_V1, 'v1');\n\n\tif (!result.success) {\n\t\treturn problems.badRequest(c, result.error || 'Invalid or expired reset token');\n\t}\n\n\tlogSecurityEvent('password_reset_completed', 'medium', {});\n\n\treturn c.json({ success: true, redirect: '/login' });\n};\n\nexport const resetPasswordMiddleware = [\n\trateLimit({\n\t\tidentifier: async (c) => c.req.header('cf-connecting-ip') || 'unknown',\n\t\taction: 'reset_password',\n\t\tmaxAttempts: 5,\n\t\twindowMs: 3600000, // 1 hour\n\t}),\n];\n","import { createRoute } from '@hono/zod-openapi';\nimport { requireAuth } from '../middleware/auth';\nimport { logSecurityEvent } from '../lib/logger';\nimport { validatePassword } from '../core/password';\nimport { changePassword } from '../core/change-password';\nimport { getSessionCookieName } from '../core/cookies';\nimport { problems } from '../lib/problem-json';\nimport {\n\tchangePasswordRequestSchema,\n\tchangePasswordResponseSchema,\n\terrorResponseSchema,\n} from './schemas';\n\nexport const changePasswordRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/change-password',\n\ttags: ['Authentication'],\n\tsummary: 'Change password (authenticated)',\n\tdescription:\n\t\t'Changes the password for the authenticated user. Requires current password. Invalidates all other sessions.',\n\tsecurity: [{ cookieAuth: [] }],\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: changePasswordRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Password changed successfully',\n\t\t\tcontent: { 'application/json': { schema: changePasswordResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid input or incorrect current password',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Not authenticated',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const changePasswordHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c, 'Not authenticated');\n\n\tconst { currentPassword, newPassword } = c.req.valid('json');\n\tconst database = c.get('db');\n\tconst env = c.env;\n\n\t// Validate new password strength\n\tconst passwordValidation = validatePassword(newPassword);\n\tif (!passwordValidation.valid) {\n\t\treturn problems.badRequest(c, passwordValidation.error || 'Invalid password');\n\t}\n\n\t// Get current session ID from cookie\n\tconst cookieHeader = c.req.header('cookie');\n\tconst cookieName = getSessionCookieName(env);\n\tconst cookiePattern = new RegExp(`${cookieName}=([^;]+)`);\n\tconst sessionIdMatch = cookieHeader?.match(cookiePattern);\n\tconst currentSessionId = sessionIdMatch?.[1] || '';\n\n\t// Change password\n\tconst result = await changePassword({\n\t\tuserId,\n\t\tcurrentPassword,\n\t\tnewPassword,\n\t\tcurrentSessionId,\n\t\tpepper: env.PASSWORD_PEPPER_V1,\n\t\tdb: database,\n\t});\n\n\tif (!result.success) {\n\t\treturn problems.badRequest(c, result.error || 'Failed to change password');\n\t}\n\n\tlogSecurityEvent('password_changed', 'medium', { userId });\n\n\treturn c.json({ success: true });\n};\n\nexport const changePasswordMiddleware = [requireAuth];\n","import { eq, and, ne } from 'drizzle-orm';\nimport type { PgTableWithColumns } from 'drizzle-orm/pg-core';\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype DrizzleDB = any;\nimport { verifyPassword, hashPassword } from './password';\nimport { logSecurityEvent } from '../lib/logger';\n\n// Table type definitions for dependency injection\ntype UsersTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\thashedPassword: any;\n\t\tupdatedAt: any;\n\t};\n\tdialect: 'pg';\n}>;\n\ntype SessionsTable = PgTableWithColumns<{\n\tname: string;\n\tschema: undefined;\n\tcolumns: {\n\t\tid: any;\n\t\tuserId: any;\n\t};\n\tdialect: 'pg';\n}>;\n\nexport interface ChangePasswordTables {\n\tusers: UsersTable;\n\tsessions: SessionsTable;\n}\n\ninterface ChangePasswordParams {\n\tuserId: string;\n\tcurrentPassword: string;\n\tnewPassword: string;\n\tcurrentSessionId: string;\n\tpepper: string;\n\tdb: DrizzleDB;\n\ttables: ChangePasswordTables;\n}\n\ninterface ChangePasswordResult {\n\tsuccess: boolean;\n\terror?: string;\n}\n\n/**\n * Change user's password and invalidate all other sessions\n * @param params - Parameters for password change\n * @returns Result with success status or error message\n */\nexport async function changePassword(params: ChangePasswordParams): Promise<ChangePasswordResult> {\n\tconst { userId, currentPassword, newPassword, currentSessionId, pepper, db, tables } = params;\n\tconst { users, sessions } = tables;\n\n\t// 1. Get user's current password hash\n\tconst [user] = await db\n\t\t.select({\n\t\t\tid: users.id,\n\t\t\thashedPassword: users.hashedPassword,\n\t\t})\n\t\t.from(users)\n\t\t.where(eq(users.id, userId))\n\t\t.limit(1);\n\n\tif (!user || !user.hashedPassword) {\n\t\treturn { success: false, error: 'User not found' };\n\t}\n\n\t// 2. Verify current password\n\tconst isValid = await verifyPassword(currentPassword, user.hashedPassword, pepper);\n\tif (!isValid) {\n\t\tlogSecurityEvent('password_change_failed', 'medium', {\n\t\t\tuserId,\n\t\t\treason: 'incorrect_current_password',\n\t\t});\n\t\treturn { success: false, error: 'Current password is incorrect' };\n\t}\n\n\t// 3. Hash new password\n\tconst newHashedPassword = await hashPassword(newPassword, pepper);\n\n\t// 4. Update password\n\tawait db\n\t\t.update(users)\n\t\t.set({\n\t\t\thashedPassword: newHashedPassword,\n\t\t\tupdatedAt: new Date(),\n\t\t})\n\t\t.where(eq(users.id, userId));\n\n\t// 5. Delete all other sessions (keep current session)\n\tawait db\n\t\t.delete(sessions)\n\t\t.where(and(eq(sessions.userId, userId), ne(sessions.id, currentSessionId)));\n\n\t// 6. Log successful password change\n\tlogSecurityEvent('password_changed', 'low', {\n\t\tuserId,\n\t});\n\n\treturn { success: true };\n}\n","import { createRoute } from '@hono/zod-openapi';\nimport { requireAuth } from '../middleware/auth';\nimport { problems } from '../lib/problem-json';\nimport { heartbeatResponseSchema, errorResponseSchema } from './schemas';\n\nexport const heartbeatRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/heartbeat',\n\ttags: ['Authentication'],\n\tsummary: 'Refresh session',\n\tdescription: 'Updates the last activity timestamp on the current session to keep it alive.',\n\tsecurity: [{ cookieAuth: [] }],\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Session refreshed successfully',\n\t\t\tcontent: { 'application/json': { schema: heartbeatResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Not authenticated',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const heartbeatHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c, 'Not authenticated');\n\n\t// Session is automatically refreshed by the auth middleware\n\treturn c.json({ success: true, timestamp: Date.now() });\n};\n\nexport const heartbeatMiddleware = [requireAuth];\n","import { createRoute } from '@hono/zod-openapi';\nimport { z } from '@hono/zod-openapi';\nimport { requireAuth } from '../middleware/auth';\nimport { requestEmailChange } from '../core/email-change';\nimport { EmailService } from '../lib/email';\nimport { logSecurityEvent } from '../lib/logger';\nimport { problems } from '../lib/problem-json';\nimport { errorResponseSchema } from './schemas';\n\nconst changeEmailRequestSchema = z\n\t.object({\n\t\tpassword: z.string().min(1).openapi({ example: 'CurrentPass123!' }),\n\t\tnewEmail: z.string().email().openapi({ example: 'newemail@example.com' }),\n\t})\n\t.openapi('ChangeEmailRequest');\n\nconst changeEmailResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('ChangeEmailResponse');\n\nexport const changeEmailRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/change-email',\n\ttags: ['Authentication'],\n\tsummary: 'Request email change (authenticated)',\n\tdescription:\n\t\t'Validates current password and sends a confirmation link to the new email address. Also sends a cancellation link to the current email.',\n\tsecurity: [{ cookieAuth: [] }],\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: changeEmailRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Confirmation email sent to new address',\n\t\t\tcontent: { 'application/json': { schema: changeEmailResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid input or email already in use',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Not authenticated or incorrect password',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const changeEmailHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c, 'Not authenticated');\n\n\tconst { password, newEmail } = c.req.valid('json');\n\tconst database = c.get('db');\n\tconst env = c.env;\n\tconst appUrl = env.APP_URL || 'http://localhost:5173';\n\n\tconst result = await requestEmailChange({\n\t\tuserId,\n\t\tpassword,\n\t\tnewEmail,\n\t\tpepper: env.PASSWORD_PEPPER_V1,\n\t\tdb: database,\n\t});\n\n\tif (!result.success) {\n\t\tif (result.error === 'Incorrect password') {\n\t\t\treturn problems.unauthorized(c, result.error);\n\t\t}\n\t\treturn problems.badRequest(c, result.error || 'Failed to request email change');\n\t}\n\n\tconst emailService = new EmailService(env);\n\n\t// Send confirmation link to new address — block if it can't receive mail\n\tconst confirmUrl = `${appUrl}/confirm-email-change?token=${result.confirmToken}`;\n\tconst confirmResult = await emailService.sendEmailChangeConfirmation(\n\t\t{ newEmail, confirmUrl },\n\t\tdatabase\n\t);\n\n\tif (!confirmResult.success) {\n\t\treturn problems.badRequest(\n\t\t\tc,\n\t\t\t'The new email address could not receive our confirmation email. Please use a different address.'\n\t\t);\n\t}\n\n\t// Send cancellation link to current address — best-effort, not blocking\n\tconst cancelUrl = `${appUrl}/cancel-email-change?token=${result.cancelToken}`;\n\tawait emailService.sendEmailChangeNotification(\n\t\t{ oldEmail: result.oldEmail!, newEmail, cancelUrl },\n\t\tdatabase\n\t);\n\n\tlogSecurityEvent('email_change_requested', 'medium', { userId });\n\n\treturn c.json({ success: true });\n};\n\nexport const changeEmailMiddleware = [requireAuth];\n","import { createRoute } from '@hono/zod-openapi';\nimport { z } from '@hono/zod-openapi';\nimport { confirmEmailChange } from '../core/email-change';\nimport { problems } from '../lib/problem-json';\nimport { errorResponseSchema } from './schemas';\n\nconst confirmEmailChangeRequestSchema = z\n\t.object({\n\t\ttoken: z.string().min(1).openapi({ example: 'a1b2c3d4...' }),\n\t})\n\t.openapi('ConfirmEmailChangeRequest');\n\nconst confirmEmailChangeResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('ConfirmEmailChangeResponse');\n\nexport const confirmEmailChangeRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/confirm-email-change',\n\ttags: ['Authentication'],\n\tsummary: 'Confirm email change',\n\tdescription:\n\t\t'Validates the confirmation token and updates the user email. The token is sent to the new email address.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: confirmEmailChangeRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Email updated successfully',\n\t\t\tcontent: { 'application/json': { schema: confirmEmailChangeResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid or expired token',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const confirmEmailChangeHandler = async (c: any) => {\n\tconst { token } = c.req.valid('json');\n\tconst database = c.get('db');\n\n\tconst result = await confirmEmailChange({ token, db: database });\n\n\tif (!result.success) {\n\t\treturn problems.badRequest(c, result.error || 'Invalid or expired token');\n\t}\n\n\treturn c.json({ success: true });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { z } from '@hono/zod-openapi';\nimport { cancelEmailChange } from '../core/email-change';\nimport { problems } from '../lib/problem-json';\nimport { errorResponseSchema } from './schemas';\n\nconst cancelEmailChangeRequestSchema = z\n\t.object({\n\t\ttoken: z.string().min(1).openapi({ example: 'a1b2c3d4...' }),\n\t})\n\t.openapi('CancelEmailChangeRequest');\n\nconst cancelEmailChangeResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('CancelEmailChangeResponse');\n\nexport const cancelEmailChangeRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/cancel-email-change',\n\ttags: ['Authentication'],\n\tsummary: 'Cancel pending email change',\n\tdescription:\n\t\t'Validates the cancellation token and removes the pending email change request. The token is sent to the old email address.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: cancelEmailChangeRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Email change cancelled',\n\t\t\tcontent: { 'application/json': { schema: cancelEmailChangeResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid or expired token',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const cancelEmailChangeHandler = async (c: any) => {\n\tconst { token } = c.req.valid('json');\n\tconst database = c.get('db');\n\n\tconst result = await cancelEmailChange({ token, db: database });\n\n\tif (!result.success) {\n\t\treturn problems.badRequest(c, result.error || 'Invalid or expired token');\n\t}\n\n\treturn c.json({ success: true });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { z } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { requireAuth } from '../middleware/auth';\nimport { verifyPasswordWithRotation } from '../core/password';\nimport { clearSessionCookie } from '../core/cookies';\nimport { logSecurityEvent, logError } from '../lib/logger';\nimport { problems } from '../lib/problem-json';\nimport { getAuthContext } from '../factory';\nimport { errorResponseSchema } from './schemas';\n\nconst deleteAccountRequestSchema = z\n\t.object({\n\t\tpassword: z.string().min(1).openapi({ example: 'CurrentPass123!' }),\n\t})\n\t.openapi('DeleteAccountRequest');\n\nconst deleteAccountResponseSchema = z\n\t.object({\n\t\tmessage: z.string().openapi({ example: 'Account scheduled for deletion' }),\n\t\tpurgeAt: z.string().datetime().openapi({ example: '2024-02-14T10:30:00Z' }),\n\t\trecoveryWindowDays: z.number().int().openapi({ example: 30 }),\n\t})\n\t.openapi('DeleteAccountResponse');\n\nexport const deleteAccountRoute = createRoute({\n\tmethod: 'delete',\n\tpath: '/account',\n\ttags: ['Authentication'],\n\tsummary: 'Delete account',\n\tdescription:\n\t\t'Soft-deletes the authenticated user account. Sets a 30-day purge window during which the account can be recovered by logging in. Requires password confirmation.',\n\tsecurity: [{ cookieAuth: [] }],\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: deleteAccountRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Account scheduled for deletion',\n\t\t\tcontent: { 'application/json': { schema: deleteAccountResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid input',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Not authenticated or incorrect password',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\nconst PURGE_WINDOW_DAYS = 30;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const deleteAccountHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c, 'Not authenticated');\n\n\tconst { password } = c.req.valid('json');\n\tconst { db: database, schema } = getAuthContext(c);\n\tconst env = c.env;\n\n\ttry {\n\t\tconst [user] = await database\n\t\t\t.select({\n\t\t\t\tid: schema.users.id,\n\t\t\t\thashedPassword: schema.users.hashedPassword,\n\t\t\t})\n\t\t\t.from(schema.users)\n\t\t\t.where(eq(schema.users.id, userId))\n\t\t\t.limit(1);\n\n\t\tif (!user || !user.hashedPassword) {\n\t\t\treturn problems.unauthorized(c, 'Invalid credentials');\n\t\t}\n\n\t\tconst { verified: isValid } = await verifyPasswordWithRotation(\n\t\t\tpassword,\n\t\t\tuser.hashedPassword,\n\t\t\tenv.PASSWORD_PEPPER_V1,\n\t\t\tenv.PASSWORD_PEPPER_V2\n\t\t);\n\t\tif (!isValid) {\n\t\t\treturn problems.unauthorized(c, 'Incorrect password');\n\t\t}\n\n\t\tconst now = new Date();\n\t\tconst purgeAt = new Date(now.getTime() + PURGE_WINDOW_DAYS * 24 * 60 * 60 * 1000);\n\n\t\tawait database\n\t\t\t.update(schema.users)\n\t\t\t.set({ deletedAt: now, scheduledPurgeAt: purgeAt, updatedAt: now })\n\t\t\t.where(eq(schema.users.id, userId));\n\n\t\tawait database.delete(schema.sessions).where(eq(schema.sessions.userId, userId));\n\n\t\tlogSecurityEvent('account_deletion_requested', 'high', { userId });\n\n\t\treturn c.json(\n\t\t\t{\n\t\t\t\tmessage: 'Account scheduled for deletion',\n\t\t\t\tpurgeAt: purgeAt.toISOString(),\n\t\t\t\trecoveryWindowDays: PURGE_WINDOW_DAYS,\n\t\t\t},\n\t\t\t200,\n\t\t\t{ 'Set-Cookie': clearSessionCookie(env) }\n\t\t);\n\t} catch (error) {\n\t\tlogError(error as Error, { context: 'account_deletion', userId });\n\t\treturn problems.internalError(c, error as Error);\n\t}\n};\n\nexport const deleteAccountMiddleware = [requireAuth];\n","import { createRoute } from '@hono/zod-openapi';\nimport { z } from '@hono/zod-openapi';\nimport { eq, and, gt } from 'drizzle-orm';\nimport { getAuthContext } from '../factory';\nimport { createSession, deleteSession } from '../core/session';\nimport { AUTH_DEFAULTS } from '../core/config';\nimport { problems } from '../lib/problem-json';\nimport logger from '../lib/logger';\nimport { errorResponseSchema } from './schemas';\n\nconst refreshRequestSchema = z\n\t.object({\n\t\trefreshToken: z.string().min(1).openapi({ example: 'abc123...' }),\n\t})\n\t.openapi('RefreshRequest');\n\nconst refreshResponseSchema = z\n\t.object({\n\t\taccessToken: z.string().openapi({ example: 'xyz789...' }),\n\t\texpiresIn: z.number().int().openapi({ example: 604800 }),\n\t})\n\t.openapi('RefreshResponse');\n\nexport const refreshRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/refresh',\n\ttags: ['Authentication'],\n\tsummary: 'Refresh access token',\n\tdescription:\n\t\t'Exchange a valid session token for a new one. Used by mobile apps for token rotation.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: refreshRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Token refreshed successfully',\n\t\t\tcontent: { 'application/json': { schema: refreshResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Invalid or expired token',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const refreshHandler = async (c: any) => {\n\tconst { refreshToken } = c.req.valid('json');\n\tconst { db, schema } = getAuthContext(c);\n\n\t// Validate the refresh token (which is the session ID)\n\tconst [session] = await db\n\t\t.select({\n\t\t\tid: schema.sessions.id,\n\t\t\tuserId: schema.sessions.userId,\n\t\t\texpiresAt: schema.sessions.expiresAt,\n\t\t\tfingerprint: schema.sessions.fingerprint,\n\t\t\tipAddress: schema.sessions.ipAddress,\n\t\t})\n\t\t.from(schema.sessions)\n\t\t.where(and(eq(schema.sessions.id, refreshToken), gt(schema.sessions.expiresAt, Date.now())))\n\t\t.limit(1);\n\n\tif (!session) {\n\t\treturn problems.unauthorized(c, 'Invalid or expired refresh token');\n\t}\n\n\t// Rotate the session: delete old, create new\n\tawait deleteSession(db, session.id);\n\n\tconst newSessionId = await createSession(\n\t\tdb,\n\t\t{ sessions: schema.sessions },\n\t\tsession.userId,\n\t\tsession.fingerprint || undefined,\n\t\tsession.ipAddress || undefined\n\t);\n\n\tlogger.info('Session refreshed', { userId: session.userId });\n\n\t// Return the new token (expiresIn in seconds)\n\treturn c.json({\n\t\taccessToken: newSessionId,\n\t\texpiresIn: AUTH_DEFAULTS.SESSION_TTL_DAYS * 24 * 60 * 60,\n\t});\n};\n\nexport const refreshMiddleware = [];\n","import { createRoute } from '@hono/zod-openapi';\nimport { z } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { rateLimit } from '../middleware/rateLimit';\nimport { requireAuth } from '../middleware/auth';\nimport logger from '../lib/logger';\nimport { getAuthContext } from '../factory';\nimport { generateSecureToken, hashToken } from '../core/tokens';\nimport { EmailService } from '../lib/email';\nimport { problems } from '../lib/problem-json';\n\nconst resendVerificationResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tmessage: z.string().openapi({ example: 'Verification email sent' }),\n\t})\n\t.openapi('ResendVerificationResponse');\n\nconst errorResponseSchema = z\n\t.object({\n\t\ttype: z.string(),\n\t\ttitle: z.string(),\n\t\tstatus: z.number(),\n\t\tdetail: z.string().optional(),\n\t})\n\t.openapi('ErrorResponse');\n\nexport const resendVerificationRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/resend-verification',\n\ttags: ['Authentication'],\n\tsummary: 'Resend email verification',\n\tdescription: 'Resends the email verification link to the authenticated user.',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Verification email sent',\n\t\t\tcontent: { 'application/json': { schema: resendVerificationResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Email already verified',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Not authenticated',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t429: {\n\t\t\tdescription: 'Rate limit exceeded',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const resendVerificationHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tconst { db, schema } = getAuthContext(c);\n\tconst env = c.env;\n\n\tconst [user] = await db\n\t\t.select({\n\t\t\tid: schema.users.id,\n\t\t\temail: schema.users.email,\n\t\t\tname: schema.users.name,\n\t\t\temailVerified: schema.users.emailVerified,\n\t\t})\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, userId))\n\t\t.limit(1);\n\n\tif (!user) {\n\t\treturn problems.unauthorized(c, 'User not found');\n\t}\n\n\tif (user.emailVerified) {\n\t\treturn problems.badRequest(c, 'Email is already verified');\n\t}\n\n\t// Delete any existing verification tokens for this user\n\tawait db.delete(schema.emailVerificationTokens).where(eq(schema.emailVerificationTokens.userId, userId));\n\n\t// Generate new token\n\tconst emailToken = generateSecureToken(32);\n\tconst tokenHash = await hashToken(emailToken);\n\tconst expiresAt = Date.now() + 24 * 60 * 60 * 1000; // 24 hours\n\n\tawait db.insert(schema.emailVerificationTokens).values({\n\t\ttokenHash,\n\t\tuserId: user.id,\n\t\temail: user.email,\n\t\texpiresAt,\n\t\tcreatedAt: Date.now(),\n\t});\n\n\t// Send verification email\n\tconst emailService = new EmailService(env);\n\tconst verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;\n\tawait emailService.sendVerificationEmail(\n\t\t{\n\t\t\temail: user.email,\n\t\t\ttoken: emailToken,\n\t\t\tverificationUrl,\n\t\t\tfirstName: user.name || undefined,\n\t\t},\n\t\tdb\n\t);\n\n\tlogger.info('Verification email resent', { userId: user.id, email: user.email });\n\n\treturn c.json({\n\t\tsuccess: true,\n\t\tmessage: 'Verification email sent',\n\t});\n};\n\nexport const resendVerificationMiddleware = [\n\trequireAuth,\n\trateLimit({\n\t\tidentifier: async (c) => c.get('userId') || 'unknown',\n\t\taction: 'resend_verification',\n\t\tmaxAttempts: 3,\n\t\twindowMs: 3600000, // 1 hour - max 3 resends per hour\n\t}),\n];\n","// 2FA domain router\n// Mounts all 2FA routes with OpenAPI route definitions\n\nimport { OpenAPIHono } from '@hono/zod-openapi';\nimport type { Env, Variables } from '../../types';\nimport { csrf } from '../../middleware/csrf';\nimport { requireAuth } from '../../middleware/auth';\nimport { rateLimit } from '../../middleware/rateLimit';\n\nimport { statusRoute, statusHandler } from './status';\nimport { totpSetupRoute, totpSetupHandler } from './totp-setup';\nimport { totpVerifyRoute, totpVerifyHandler } from './totp-verify';\nimport { totpDisableRoute, totpDisableHandler } from './totp-disable';\nimport { emailSetupRoute, emailSetupHandler } from './email-setup';\nimport { emailVerifyRoute, emailVerifyHandler } from './email-verify';\nimport { emailSendCodeRoute, emailSendCodeHandler } from './email-send-code';\nimport { emailDisableRoute, emailDisableHandler } from './email-disable';\nimport {\n\ttrustedDevicesGetRoute,\n\ttrustedDevicesGetHandler,\n\ttrustedDevicesDeleteRoute,\n\ttrustedDevicesDeleteHandler,\n} from './trusted-devices';\nimport { backupCodesRegenerateRoute, backupCodesRegenerateHandler } from './backup-codes';\nimport { challengeRoute, challengeHandler } from './challenge';\nimport { challengeResendRoute, challengeResendHandler } from './challenge-resend';\n\nconst twoFa = new OpenAPIHono<{ Bindings: Env; Variables: Variables }>();\n\ntwoFa.use('*', csrf);\n\n// Status endpoint (requires auth)\ntwoFa.use('/status', requireAuth);\ntwoFa.openapi(statusRoute, statusHandler);\n\n// TOTP endpoints (requires auth)\ntwoFa.use('/totp/setup', requireAuth);\ntwoFa.openapi(totpSetupRoute, totpSetupHandler);\n\ntwoFa.use('/totp/verify', requireAuth);\ntwoFa.openapi(totpVerifyRoute, totpVerifyHandler);\n\ntwoFa.use(\n\t'/totp/disable',\n\trequireAuth,\n\trateLimit({\n\t\tidentifier: async (c) => c.get('userId') || 'unknown',\n\t\taction: '2fa_disable',\n\t\tmaxAttempts: 5,\n\t\twindowMs: 900000, // 5 attempts per 15 minutes\n\t})\n);\ntwoFa.openapi(totpDisableRoute, totpDisableHandler);\n\n// Email 2FA endpoints (requires auth)\ntwoFa.use('/email/setup', requireAuth);\ntwoFa.openapi(emailSetupRoute, emailSetupHandler);\n\ntwoFa.use('/email/verify', requireAuth);\ntwoFa.openapi(emailVerifyRoute, emailVerifyHandler);\n\ntwoFa.use(\n\t'/email/send-code',\n\trequireAuth,\n\trateLimit({\n\t\tidentifier: async (c) => c.get('userId') || 'unknown',\n\t\taction: '2fa_email_send',\n\t\tmaxAttempts: 3,\n\t\twindowMs: 300000, // 3 per 5 minutes\n\t})\n);\ntwoFa.openapi(emailSendCodeRoute, emailSendCodeHandler);\n\ntwoFa.use(\n\t'/email/disable',\n\trequireAuth,\n\trateLimit({\n\t\tidentifier: async (c) => c.get('userId') || 'unknown',\n\t\taction: '2fa_disable',\n\t\tmaxAttempts: 5,\n\t\twindowMs: 900000, // 5 attempts per 15 minutes\n\t})\n);\ntwoFa.openapi(emailDisableRoute, emailDisableHandler);\n\n// Trusted devices endpoints (requires auth)\ntwoFa.use('/trusted-devices', requireAuth);\ntwoFa.openapi(trustedDevicesGetRoute, trustedDevicesGetHandler);\n\ntwoFa.use('/trusted-devices/:id', requireAuth);\ntwoFa.openapi(trustedDevicesDeleteRoute, trustedDevicesDeleteHandler);\n\n// Backup codes endpoint (requires auth + rate limit)\ntwoFa.use(\n\t'/backup-codes/regenerate',\n\trequireAuth,\n\trateLimit({\n\t\tidentifier: async (c) => c.get('userId') || 'unknown',\n\t\taction: '2fa_backup_regenerate',\n\t\tmaxAttempts: 3,\n\t\twindowMs: 3600000, // 3 per hour per user\n\t})\n);\ntwoFa.openapi(backupCodesRegenerateRoute, backupCodesRegenerateHandler);\n\n// Challenge endpoints (no auth required - uses challenge cookie)\ntwoFa.use(\n\t'/challenge',\n\trateLimit({\n\t\tidentifier: async (c) => c.req.header('cf-connecting-ip') || 'unknown',\n\t\taction: '2fa_challenge',\n\t\tmaxAttempts: 10,\n\t\twindowMs: 300000, // 10 per 5 min per IP\n\t})\n);\ntwoFa.openapi(challengeRoute, challengeHandler);\n\ntwoFa.use(\n\t'/challenge/resend',\n\trateLimit({\n\t\tidentifier: async (c) => c.req.header('cf-connecting-ip') || 'unknown',\n\t\taction: '2fa_resend',\n\t\tmaxAttempts: 3,\n\t\twindowMs: 300000, // 3 per 5 min per IP\n\t})\n);\ntwoFa.openapi(challengeResendRoute, challengeResendHandler);\n\nexport default twoFa;\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq, and, isNull } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { problems } from '../../lib/problem-json';\nimport { statusResponseSchema, errorResponseSchema } from './schemas';\nimport { getUserEnabled2faMethods } from './helpers';\n\nexport const statusRoute = createRoute({\n\tmethod: 'get',\n\tpath: '/status',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Get 2FA status',\n\tdescription: 'Get current 2FA configuration and available methods for the authenticated user',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: '2FA status retrieved successfully',\n\t\t\tcontent: { 'application/json': { schema: statusResponseSchema } },\n\t\t\theaders: {\n\t\t\t\t'Cache-Control': {\n\t\t\t\t\tdescription: 'Cache for 24 hours',\n\t\t\t\t\tschema: {\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\texample: 'private, max-age=86400, stale-while-revalidate=3600',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const statusHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { db, schema } = getAuthContext(c);\n\tconst methods = await getUserEnabled2faMethods(db, userId, schema.user2faMethods);\n\n\tconst backupCodes = await db\n\t\t.select({ id: schema.userBackupCodes.id })\n\t\t.from(schema.userBackupCodes)\n\t\t.where(and(eq(schema.userBackupCodes.userId, userId), isNull(schema.userBackupCodes.usedAt)));\n\n\tc.header('Cache-Control', 'private, max-age=86400, stale-while-revalidate=3600');\n\tc.header('Vary', 'Cookie');\n\n\treturn c.json({\n\t\tenabled: methods.length > 0,\n\t\tmethods: methods.map((m) => m.method),\n\t\tbackupCodesRemaining: backupCodes.length,\n\t});\n};\n","// 2FA domain Zod schemas for OpenAPI documentation\n// All 2FA route request/response schemas with .openapi() decorators for automatic spec generation\n\nimport { z } from '@hono/zod-openapi';\n\n// ============================================\n// Request Schemas\n// ============================================\n\n/**\n * TOTP code validation (6-digit numeric code)\n */\nexport const totpCodeSchema = z\n\t.object({\n\t\tcode: z\n\t\t\t.string()\n\t\t\t.length(6)\n\t\t\t.regex(/^\\d{6}$/, 'Code must be 6 digits')\n\t\t\t.openapi({ example: '123456' }),\n\t})\n\t.openapi('TotpCodeRequest');\n\n/**\n * Email code validation (6-digit numeric code)\n */\nexport const emailCodeSchema = z\n\t.object({\n\t\tcode: z\n\t\t\t.string()\n\t\t\t.length(6)\n\t\t\t.regex(/^\\d{6}$/, 'Code must be 6 digits')\n\t\t\t.openapi({ example: '789012' }),\n\t})\n\t.openapi('EmailCodeRequest');\n\n/**\n * POST /v1/auth/2fa/disable\n * Disable a 2FA method\n */\nexport const disableRequestSchema = z\n\t.object({\n\t\tcode: z\n\t\t\t.string()\n\t\t\t.length(6)\n\t\t\t.regex(/^\\d{6}$/, 'Code must be 6 digits')\n\t\t\t.openapi({ example: '123456' }),\n\t\tmethod: z.enum(['totp', 'email', 'backup']).openapi({ example: 'totp' }),\n\t})\n\t.openapi('DisableRequest');\n\n/**\n * POST /v1/auth/2fa/verify\n * Verify 2FA code during login challenge\n */\nexport const challengeRequestSchema = z\n\t.object({\n\t\tcode: z\n\t\t\t.string()\n\t\t\t.length(6)\n\t\t\t.regex(/^\\d{6}$/, 'Code must be 6 digits')\n\t\t\t.openapi({ example: '123456' }),\n\t\tmethod: z.enum(['totp', 'email', 'backup']).openapi({ example: 'totp' }),\n\t\trememberDevice: z.boolean().optional().openapi({ example: true }),\n\t})\n\t.openapi('ChallengeRequest');\n\n// ============================================\n// Response Schemas\n// ============================================\n\n/**\n * GET /v1/auth/2fa/status response\n * Current 2FA configuration for the user\n */\nexport const statusResponseSchema = z\n\t.object({\n\t\tenabled: z.boolean().openapi({ example: true }),\n\t\tmethods: z.array(z.enum(['totp', 'email'])).openapi({ example: ['totp', 'email'] }),\n\t\tbackupCodesRemaining: z.number().int().min(0).openapi({ example: 8 }),\n\t})\n\t.openapi('StatusResponse');\n\n/**\n * POST /v1/auth/2fa/totp/setup response\n * TOTP setup data (secret + QR code URI)\n */\nexport const totpSetupResponseSchema = z\n\t.object({\n\t\tsecret: z.string().openapi({ example: 'JBSWY3DPEHPK3PXP' }),\n\t\tqrCodeUri: z.string().url().openapi({\n\t\t\texample: 'otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp',\n\t\t}),\n\t})\n\t.openapi('TotpSetupResponse');\n\n/**\n * POST /v1/auth/2fa/totp/verify response\n * TOTP verification result with optional backup codes\n */\nexport const totpVerifyResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tbackupCodes: z\n\t\t\t.array(z.string())\n\t\t\t.optional()\n\t\t\t.openapi({ example: ['abc12345', 'def67890', 'ghi13579', 'jkl24680'] }),\n\t})\n\t.openapi('TotpVerifyResponse');\n\n/**\n * POST /v1/auth/2fa/email/setup response\n * Email 2FA setup confirmation\n */\nexport const emailSetupResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('EmailSetupResponse');\n\n/**\n * POST /v1/auth/2fa/email/verify response\n * Email verification result with optional backup codes\n */\nexport const emailVerifyResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tbackupCodes: z\n\t\t\t.array(z.string())\n\t\t\t.optional()\n\t\t\t.openapi({ example: ['abc12345', 'def67890', 'ghi13579', 'jkl24680'] }),\n\t})\n\t.openapi('EmailVerifyResponse');\n\n/**\n * POST /v1/auth/2fa/email/send-code response\n * Email code sent confirmation\n */\nexport const sendCodeResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('SendCodeResponse');\n\n/**\n * POST /v1/auth/2fa/disable response\n * 2FA method disabled confirmation\n */\nexport const disableResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tsessionInvalidated: z.boolean().optional().openapi({ example: false }),\n\t})\n\t.openapi('DisableResponse');\n\n/**\n * Trusted device object\n */\nexport const trustedDeviceSchema = z\n\t.object({\n\t\tid: z.string().uuid().openapi({ example: 'a1b2c3d4-e5f6-47a8-9b0c-1d2e3f4a5b6c' }),\n\t\tdeviceName: z.string().openapi({ example: 'Chrome on MacBook Pro' }),\n\t\tdeviceType: z.enum(['desktop', 'mobile', 'tablet']).openapi({ example: 'desktop' }),\n\t\tipAddress: z.string().openapi({ example: '192.168.1.100' }),\n\t\tlastUsedAt: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.openapi({ example: 1705315800000, description: 'Unix timestamp in milliseconds' }),\n\t\tcreatedAt: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.openapi({ example: 1705229400000, description: 'Unix timestamp in milliseconds' }),\n\t})\n\t.openapi('TrustedDevice');\n\n/**\n * GET /v1/auth/2fa/trusted-devices response\n * List of all trusted devices\n */\nexport const trustedDevicesResponseSchema = z\n\t.object({\n\t\tdevices: z.array(trustedDeviceSchema).openapi({\n\t\t\texample: [\n\t\t\t\t{\n\t\t\t\t\tid: 'a1b2c3d4-e5f6-47a8-9b0c-1d2e3f4a5b6c',\n\t\t\t\t\tdeviceName: 'Chrome on MacBook Pro',\n\t\t\t\t\tdeviceType: 'desktop',\n\t\t\t\t\tipAddress: '192.168.1.100',\n\t\t\t\t\tlastUsedAt: 1705315800000,\n\t\t\t\t\tcreatedAt: 1705229400000,\n\t\t\t\t},\n\t\t\t],\n\t\t}),\n\t})\n\t.openapi('TrustedDevicesResponse');\n\n/**\n * DELETE /v1/auth/2fa/trusted-devices/:id response\n * Trusted device deletion confirmation\n */\nexport const deleteDeviceResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('DeleteDeviceResponse');\n\n/**\n * POST /v1/auth/2fa/backup-codes/regenerate response\n * New backup codes after regeneration\n */\nexport const regenerateBackupCodesResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t\tbackupCodes: z.array(z.string()).openapi({\n\t\t\texample: [\n\t\t\t\t'abc12345',\n\t\t\t\t'def67890',\n\t\t\t\t'ghi13579',\n\t\t\t\t'jkl24680',\n\t\t\t\t'mno98765',\n\t\t\t\t'pqr54321',\n\t\t\t\t'stu11111',\n\t\t\t\t'vwx22222',\n\t\t\t],\n\t\t}),\n\t})\n\t.openapi('RegenerateBackupCodesResponse');\n\n/**\n * POST /v1/auth/2fa/verify response\n * 2FA challenge verification result with user data\n */\nexport const challengeResponseSchema = z\n\t.object({\n\t\tuser: z\n\t\t\t.object({\n\t\t\t\tid: z.string().uuid().openapi({ example: 'a1b2c3d4-e5f6-47a8-9b0c-1d2e3f4a5b6c' }),\n\t\t\t\temail: z.string().email().openapi({ example: 'user@example.com' }),\n\t\t\t\tname: z.string().nullable().openapi({ example: 'John Doe' }),\n\t\t\t})\n\t\t\t.openapi('ChallengeUser'),\n\t})\n\t.openapi('ChallengeResponse');\n\n/**\n * POST /v1/auth/2fa/resend response\n * Code resend confirmation\n */\nexport const resendResponseSchema = z\n\t.object({\n\t\tsuccess: z.boolean().openapi({ example: true }),\n\t})\n\t.openapi('ResendResponse');\n\n/**\n * Error response for 2FA endpoints\n * RFC 9457 Problem Details format\n */\nexport const errorResponseSchema = z\n\t.object({\n\t\ttype: z.string().url().openapi({\n\t\t\texample: 'https://api.example.com/errors/bad-request',\n\t\t\tdescription: 'URI identifying the problem type',\n\t\t}),\n\t\ttitle: z.string().openapi({\n\t\t\texample: 'Bad Request',\n\t\t\tdescription: 'Short, human-readable summary of the problem type',\n\t\t}),\n\t\tstatus: z.number().int().min(400).max(599).openapi({\n\t\t\texample: 400,\n\t\t\tdescription: 'HTTP status code',\n\t\t}),\n\t\tdetail: z.string().optional().openapi({\n\t\t\texample: 'Invalid 2FA code',\n\t\t\tdescription: 'Human-readable explanation specific to this occurrence',\n\t\t}),\n\t\tinstance: z.string().optional().openapi({\n\t\t\texample: 'POST /v1/auth/2fa/challenge',\n\t\t\tdescription: 'URI identifying the specific occurrence of the problem',\n\t\t}),\n\t\ttraceId: z.string().optional().openapi({\n\t\t\texample: 'abc123def456',\n\t\t\tdescription: 'Trace ID for debugging',\n\t\t}),\n\t\trequestId: z.string().optional().openapi({\n\t\t\texample: 'req-123',\n\t\t\tdescription: 'Request ID for correlation',\n\t\t}),\n\t})\n\t.openapi('TwoFactorErrorResponse');\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq, and } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { generateTotpSecret, generateQrCodeUri } from '../../core/2fa';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { problems } from '../../lib/problem-json';\nimport { totpSetupResponseSchema, errorResponseSchema } from './schemas';\n\nexport const totpSetupRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/totp/setup',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Setup TOTP 2FA',\n\tdescription: 'Generate TOTP secret and QR code URI for authenticator app enrollment',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'TOTP setup initiated successfully',\n\t\t\tcontent: { 'application/json': { schema: totpSetupResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'TOTP already enabled',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'User not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const totpSetupHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { db, schema, env } = getAuthContext(c);\n\n\tconst existing = await db\n\t\t.select({ id: schema.user2faMethods.id })\n\t\t.from(schema.user2faMethods)\n\t\t.where(and(eq(schema.user2faMethods.userId, userId), eq(schema.user2faMethods.method, 'totp')))\n\t\t.limit(1);\n\n\tif (existing.length > 0) {\n\t\treturn problems.badRequest(c, 'TOTP already enabled');\n\t}\n\n\tconst [user] = await db\n\t\t.select({ email: schema.users.email })\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, userId))\n\t\t.limit(1);\n\n\tif (!user) return problems.notFound(c, 'User not found');\n\n\tconst secret = generateTotpSecret();\n\tconst qrCodeUri = generateQrCodeUri(secret, user.email);\n\n\tawait env.OAUTH_STATES.put(`totp_setup:${userId}`, secret, { expirationTtl: 300 });\n\n\tlogSecurityEvent('2fa_totp_setup_initiated', 'low', { userId });\n\n\treturn c.json({ secret, qrCodeUri });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport {\n\tverifyTotpCode,\n\tencryptTotpSecret,\n\tgenerateBackupCodes,\n\thashBackupCode,\n\tformatBackupCode,\n} from '../../core/2fa';\nimport { logSecurityEvent } from '../../lib/logger';\nimport logger from '../../lib/logger';\nimport { EmailService } from '../../lib/email';\nimport { problems } from '../../lib/problem-json';\nimport { totpCodeSchema, totpVerifyResponseSchema, errorResponseSchema } from './schemas';\nimport { getUserEnabled2faMethods } from './helpers';\n\nexport const totpVerifyRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/totp/verify',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Verify TOTP setup',\n\tdescription:\n\t\t'Verify TOTP code and complete enrollment. Returns backup codes if this is the first 2FA method.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: totpCodeSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'TOTP verified and enrolled successfully',\n\t\t\tcontent: { 'application/json': { schema: totpVerifyResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid code or setup expired',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t500: {\n\t\t\tdescription: 'Server configuration error',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const totpVerifyHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { code } = c.req.valid('json');\n\tconst { db, schema, env } = getAuthContext(c);\n\n\tconst secret = await env.OAUTH_STATES.get(`totp_setup:${userId}`);\n\tif (!secret) {\n\t\treturn problems.badRequest(c, 'Setup expired. Please start again.');\n\t}\n\n\tconst result = verifyTotpCode(secret, code, null);\n\tif (!result.valid) {\n\t\tlogSecurityEvent('2fa_totp_setup_failed', 'medium', { userId });\n\t\treturn problems.badRequest(c, 'Invalid code');\n\t}\n\n\tconst [user] = await db\n\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, userId))\n\t\t.limit(1);\n\n\tconst encryptionKey = env.TOTP_ENCRYPTION_KEY;\n\tif (!encryptionKey) {\n\t\tlogger.error('TOTP_ENCRYPTION_KEY not configured');\n\t\treturn problems.internalError(c);\n\t}\n\n\tconst encryptedSecret = await encryptTotpSecret(secret, encryptionKey);\n\tconst now = Date.now();\n\n\tconst existingMethods = await getUserEnabled2faMethods(db, userId, schema.user2faMethods);\n\tconst isFirstMethod = existingMethods.length === 0;\n\n\tawait db.insert(schema.user2faMethods).values({\n\t\tuserId,\n\t\tmethod: 'totp',\n\t\ttotpSecret: encryptedSecret,\n\t\tlastTotpCounter: result.counter,\n\t\tisPrimary: isFirstMethod,\n\t\tverifiedAt: now,\n\t\tcreatedAt: now,\n\t});\n\n\tawait env.OAUTH_STATES.delete(`totp_setup:${userId}`);\n\n\tlet backupCodes: string[] | undefined;\n\tif (isFirstMethod) {\n\t\tconst codes = generateBackupCodes();\n\t\tconst hashedCodes = await Promise.all(\n\t\t\tcodes.map(async (code) => ({\n\t\t\t\tuserId,\n\t\t\t\tcodeHash: await hashBackupCode(code),\n\t\t\t\tcreatedAt: now,\n\t\t\t}))\n\t\t);\n\t\tawait db.insert(schema.userBackupCodes).values(hashedCodes);\n\t\tbackupCodes = codes.map(formatBackupCode);\n\t}\n\n\tif (user) {\n\t\tconst emailService = new EmailService(env);\n\t\tconst firstName = user.name?.split(' ')[0] || null;\n\t\tawait emailService.send2faEnabledEmail(\n\t\t\t{ email: user.email, firstName, method: 'totp' },\n\t\t\tdb\n\t\t);\n\t}\n\n\tlogSecurityEvent('2fa_totp_enrolled', 'high', { userId });\n\n\treturn c.json({ success: true, backupCodes });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq, and } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { invalidateAllUserSessions } from '../../core/session';\nimport { EmailService } from '../../lib/email';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { problems } from '../../lib/problem-json';\nimport { disableRequestSchema, disableResponseSchema, errorResponseSchema } from './schemas';\nimport { verifyAny2faCode, getUserEnabled2faMethods } from './helpers';\n\nexport const totpDisableRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/totp/disable',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Disable TOTP 2FA',\n\tdescription:\n\t\t'Disable TOTP 2FA method. If this is the last method, all sessions will be invalidated.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: disableRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'TOTP disabled successfully',\n\t\t\tcontent: { 'application/json': { schema: disableResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid verification code',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const totpDisableHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) {\n\t\treturn problems.unauthorized(c);\n\t}\n\n\tconst { code, method } = c.req.valid('json');\n\tconst { db, schema, env } = getAuthContext(c);\n\n\t// Verify 2FA code\n\tconst verification = await verifyAny2faCode(db, env, userId, code, method, {\n\t\tuser2faMethods: schema.user2faMethods,\n\t\tuserBackupCodes: schema.userBackupCodes,\n\t});\n\tif (!verification.valid) {\n\t\tlogSecurityEvent('2fa_disable_failed', 'medium', { userId, method: 'totp' });\n\t\treturn problems.badRequest(c, 'Invalid verification code');\n\t}\n\n\t// Delete the method\n\tawait db\n\t\t.delete(schema.user2faMethods)\n\t\t.where(and(eq(schema.user2faMethods.userId, userId), eq(schema.user2faMethods.method, 'totp')));\n\n\t// Check if any methods remain\n\tconst remaining = await getUserEnabled2faMethods(db, userId, schema.user2faMethods);\n\n\t// If no methods remain, clean up everything and invalidate sessions\n\tif (remaining.length === 0) {\n\t\tawait db.delete(schema.userBackupCodes).where(eq(schema.userBackupCodes.userId, userId));\n\t\tawait db.delete(schema.userTrustedDevices).where(eq(schema.userTrustedDevices.userId, userId));\n\n\t\t// Invalidate all sessions (security measure)\n\t\tawait invalidateAllUserSessions(db, userId);\n\n\t\t// Send disabled notification\n\t\tconst [user] = await db\n\t\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t\t.from(schema.users)\n\t\t\t.where(eq(schema.users.id, userId))\n\t\t\t.limit(1);\n\n\t\tif (user) {\n\t\t\tconst emailService = new EmailService(env);\n\t\t\tconst firstName = user.name?.split(' ')[0] || null;\n\t\t\tawait emailService.send2faDisabledEmail({ email: user.email, firstName }, db);\n\t\t}\n\n\t\tlogSecurityEvent('2fa_disabled', 'critical', { userId, lastMethod: 'totp' });\n\t\treturn c.json({ success: true, sessionInvalidated: true });\n\t}\n\n\tlogSecurityEvent('2fa_method_disabled', 'high', {\n\t\tuserId,\n\t\tmethod: 'totp',\n\t\tremainingMethods: remaining.length,\n\t});\n\n\treturn c.json({ success: true, sessionInvalidated: false });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq, and } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { hashToken } from '../../core/tokens';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { EmailService } from '../../lib/email';\nimport { problems } from '../../lib/problem-json';\nimport { emailSetupResponseSchema, errorResponseSchema } from './schemas';\nimport { generateEmailOtp } from './helpers';\n\nexport const emailSetupRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/email/setup',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Setup Email 2FA',\n\tdescription: 'Send verification code to user email to begin email 2FA enrollment',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Verification code sent successfully',\n\t\t\tcontent: { 'application/json': { schema: emailSetupResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Email 2FA already enabled',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'User not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const emailSetupHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { db, schema, env } = getAuthContext(c);\n\n\tconst existing = await db\n\t\t.select({ id: schema.user2faMethods.id })\n\t\t.from(schema.user2faMethods)\n\t\t.where(and(eq(schema.user2faMethods.userId, userId), eq(schema.user2faMethods.method, 'email')))\n\t\t.limit(1);\n\n\tif (existing.length > 0) {\n\t\treturn problems.badRequest(c, 'Email 2FA already enabled');\n\t}\n\n\tconst [user] = await db\n\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, userId))\n\t\t.limit(1);\n\n\tif (!user) return problems.notFound(c, 'User not found');\n\n\tconst code = generateEmailOtp();\n\tconst codeHash = await hashToken(code);\n\n\t// 5 minute TTL (reduced from 10)\n\tawait env.OAUTH_STATES.put(`email_2fa_setup:${userId}`, codeHash, { expirationTtl: 300 });\n\n\tconst emailService = new EmailService(env);\n\tconst firstName = user.name?.split(' ')[0] || null;\n\tawait emailService.send2faCodeEmail({ email: user.email, firstName, code }, db);\n\n\tlogSecurityEvent('2fa_email_setup_initiated', 'low', { userId });\n\n\treturn c.json({ success: true });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { hashToken } from '../../core/tokens';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { EmailService } from '../../lib/email';\nimport { generateBackupCodes, hashBackupCode, formatBackupCode } from '../../core/2fa';\nimport { problems } from '../../lib/problem-json';\nimport { emailCodeSchema, emailVerifyResponseSchema, errorResponseSchema } from './schemas';\nimport { getUserEnabled2faMethods } from './helpers';\n\nexport const emailVerifyRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/email/verify',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Verify Email 2FA setup',\n\tdescription:\n\t\t'Verify email code and complete enrollment. Returns backup codes if this is the first 2FA method.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: emailCodeSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Email 2FA verified and enrolled successfully',\n\t\t\tcontent: { 'application/json': { schema: emailVerifyResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid code or code expired',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const emailVerifyHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { code } = c.req.valid('json');\n\tconst { db, schema, env } = getAuthContext(c);\n\n\tconst storedHash = await env.OAUTH_STATES.get(`email_2fa_setup:${userId}`);\n\tif (!storedHash) {\n\t\treturn problems.badRequest(c, 'Code expired. Please request a new one.');\n\t}\n\n\tconst codeHash = await hashToken(code);\n\tif (codeHash !== storedHash) {\n\t\tlogSecurityEvent('2fa_email_setup_failed', 'medium', { userId });\n\t\treturn problems.badRequest(c, 'Invalid code');\n\t}\n\n\tconst [user] = await db\n\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, userId))\n\t\t.limit(1);\n\n\tconst now = Date.now();\n\tconst existingMethods = await getUserEnabled2faMethods(db, userId, schema.user2faMethods);\n\tconst isFirstMethod = existingMethods.length === 0;\n\n\tawait db.insert(schema.user2faMethods).values({\n\t\tuserId,\n\t\tmethod: 'email',\n\t\ttotpSecret: null,\n\t\tlastTotpCounter: null,\n\t\tisPrimary: isFirstMethod,\n\t\tverifiedAt: now,\n\t\tcreatedAt: now,\n\t});\n\n\tawait env.OAUTH_STATES.delete(`email_2fa_setup:${userId}`);\n\n\tlet backupCodes: string[] | undefined;\n\tif (isFirstMethod) {\n\t\tconst codes = generateBackupCodes();\n\t\tconst hashedCodes = await Promise.all(\n\t\t\tcodes.map(async (code) => ({\n\t\t\t\tuserId,\n\t\t\t\tcodeHash: await hashBackupCode(code),\n\t\t\t\tcreatedAt: now,\n\t\t\t}))\n\t\t);\n\t\tawait db.insert(schema.userBackupCodes).values(hashedCodes);\n\t\tbackupCodes = codes.map(formatBackupCode);\n\t}\n\n\tif (user) {\n\t\tconst emailService = new EmailService(env);\n\t\tconst firstName = user.name?.split(' ')[0] || null;\n\t\tawait emailService.send2faEnabledEmail(\n\t\t\t{ email: user.email, firstName, method: 'email' },\n\t\t\tdb\n\t\t);\n\t}\n\n\tlogSecurityEvent('2fa_email_enrolled', 'high', { userId });\n\n\treturn c.json({ success: true, backupCodes });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq, and } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { hashToken } from '../../core/tokens';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { EmailService } from '../../lib/email';\nimport { problems } from '../../lib/problem-json';\nimport { sendCodeResponseSchema, errorResponseSchema } from './schemas';\nimport { generateEmailOtp } from './helpers';\n\nexport const emailSendCodeRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/email/send-code',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Send email verification code',\n\tdescription: 'Send a new email verification code for users with email 2FA enabled',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Verification code sent successfully',\n\t\t\tcontent: { 'application/json': { schema: sendCodeResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Email 2FA not enabled',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'User not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const emailSendCodeHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { db, schema, env } = getAuthContext(c);\n\n\t// Check if user has email 2FA enabled\n\tconst [emailMethod] = await db\n\t\t.select()\n\t\t.from(schema.user2faMethods)\n\t\t.where(and(eq(schema.user2faMethods.userId, userId), eq(schema.user2faMethods.method, 'email')))\n\t\t.limit(1);\n\n\tif (!emailMethod) {\n\t\treturn problems.badRequest(c, 'Email 2FA not enabled');\n\t}\n\n\tconst [user] = await db\n\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, userId))\n\t\t.limit(1);\n\n\tif (!user) return problems.notFound(c, 'User not found');\n\n\tconst code = generateEmailOtp();\n\tconst codeHash = await hashToken(code);\n\n\t// 5 minute TTL\n\tawait env.OAUTH_STATES.put(`email_2fa_challenge:${userId}`, codeHash, {\n\t\texpirationTtl: 300,\n\t});\n\n\tconst emailService = new EmailService(env);\n\tconst firstName = user.name?.split(' ')[0] || null;\n\tconst emailResult = await emailService.send2faCodeEmail(\n\t\t{ email: user.email, firstName, code },\n\t\tdb\n\t);\n\n\tif (!emailResult.success) {\n\t\treturn problems.badRequest(\n\t\t\tc,\n\t\t\t'Your email address is not accepting messages. Please use a different 2FA method or contact support.'\n\t\t);\n\t}\n\n\tlogSecurityEvent('2fa_email_code_sent', 'low', { userId });\n\n\treturn c.json({ success: true });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq, and } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { invalidateAllUserSessions } from '../../core/session';\nimport { EmailService } from '../../lib/email';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { problems } from '../../lib/problem-json';\nimport { disableRequestSchema, disableResponseSchema, errorResponseSchema } from './schemas';\nimport { verifyAny2faCode, getUserEnabled2faMethods } from './helpers';\n\nexport const emailDisableRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/email/disable',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Disable Email 2FA',\n\tdescription:\n\t\t'Disable Email 2FA method. If this is the last method, all sessions will be invalidated.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: disableRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Email 2FA disabled successfully',\n\t\t\tcontent: { 'application/json': { schema: disableResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid verification code',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const emailDisableHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) {\n\t\treturn problems.unauthorized(c);\n\t}\n\n\tconst { code, method } = c.req.valid('json');\n\tconst { db, schema, env } = getAuthContext(c);\n\n\t// Verify 2FA code\n\tconst verification = await verifyAny2faCode(db, env, userId, code, method, {\n\t\tuser2faMethods: schema.user2faMethods,\n\t\tuserBackupCodes: schema.userBackupCodes,\n\t});\n\tif (!verification.valid) {\n\t\tlogSecurityEvent('2fa_disable_failed', 'medium', { userId, method: 'email' });\n\t\treturn problems.badRequest(c, 'Invalid verification code');\n\t}\n\n\t// Delete the method\n\tawait db\n\t\t.delete(schema.user2faMethods)\n\t\t.where(and(eq(schema.user2faMethods.userId, userId), eq(schema.user2faMethods.method, 'email')));\n\n\t// Check if any methods remain\n\tconst remaining = await getUserEnabled2faMethods(db, userId, schema.user2faMethods);\n\n\t// If no methods remain, clean up everything and invalidate sessions\n\tif (remaining.length === 0) {\n\t\tawait db.delete(schema.userBackupCodes).where(eq(schema.userBackupCodes.userId, userId));\n\t\tawait db.delete(schema.userTrustedDevices).where(eq(schema.userTrustedDevices.userId, userId));\n\n\t\t// Invalidate all sessions (security measure)\n\t\tawait invalidateAllUserSessions(db, userId);\n\n\t\t// Send disabled notification\n\t\tconst [user] = await db\n\t\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t\t.from(schema.users)\n\t\t\t.where(eq(schema.users.id, userId))\n\t\t\t.limit(1);\n\n\t\tif (user) {\n\t\t\tconst emailService = new EmailService(env);\n\t\t\tconst firstName = user.name?.split(' ')[0] || null;\n\t\t\tawait emailService.send2faDisabledEmail({ email: user.email, firstName }, db);\n\t\t}\n\n\t\tlogSecurityEvent('2fa_disabled', 'critical', { userId, lastMethod: 'email' });\n\t\treturn c.json({ success: true, sessionInvalidated: true });\n\t}\n\n\tlogSecurityEvent('2fa_method_disabled', 'high', {\n\t\tuserId,\n\t\tmethod: 'email',\n\t\tremainingMethods: remaining.length,\n\t});\n\n\treturn c.json({ success: true, sessionInvalidated: false });\n};\n","import { createRoute, z } from '@hono/zod-openapi';\nimport { eq, and, gt } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { problems } from '../../lib/problem-json';\nimport {\n\ttrustedDevicesResponseSchema,\n\tdeleteDeviceResponseSchema,\n\terrorResponseSchema,\n} from './schemas';\n\nexport const trustedDevicesGetRoute = createRoute({\n\tmethod: 'get',\n\tpath: '/trusted-devices',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'List trusted devices',\n\tdescription: 'Get all active trusted devices for the authenticated user',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Trusted devices retrieved successfully',\n\t\t\tcontent: { 'application/json': { schema: trustedDevicesResponseSchema } },\n\t\t\theaders: {\n\t\t\t\t'Cache-Control': {\n\t\t\t\t\tdescription: 'Cache for 24 hours',\n\t\t\t\t\tschema: {\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\texample: 'private, max-age=86400, stale-while-revalidate=3600',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const trustedDevicesGetHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { db, schema } = getAuthContext(c);\n\tconst now = Date.now();\n\n\tconst devices = await db\n\t\t.select({\n\t\t\tid: schema.userTrustedDevices.id,\n\t\t\tdeviceName: schema.userTrustedDevices.deviceName,\n\t\t\tdeviceType: schema.userTrustedDevices.deviceType,\n\t\t\tipAddress: schema.userTrustedDevices.ipAddress,\n\t\t\tlastUsedAt: schema.userTrustedDevices.lastUsedAt,\n\t\t\tcreatedAt: schema.userTrustedDevices.createdAt,\n\t\t})\n\t\t.from(schema.userTrustedDevices)\n\t\t.where(and(eq(schema.userTrustedDevices.userId, userId), gt(schema.userTrustedDevices.expiresAt, now)));\n\n\tc.header('Cache-Control', 'private, max-age=86400, stale-while-revalidate=3600');\n\tc.header('Vary', 'Cookie');\n\n\treturn c.json({ devices });\n};\n\nexport const trustedDevicesDeleteRoute = createRoute({\n\tmethod: 'delete',\n\tpath: '/trusted-devices/{id}',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Remove trusted device',\n\tdescription: 'Revoke a trusted device by ID',\n\trequest: {\n\t\tparams: z.object({\n\t\t\tid: z.string().uuid().openapi({ description: 'Device ID' }),\n\t\t}),\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Device removed successfully',\n\t\t\tcontent: { 'application/json': { schema: deleteDeviceResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'Device not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const trustedDevicesDeleteHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst deviceId = c.req.param('id');\n\tconst { db, schema } = getAuthContext(c);\n\n\tconst result = await db\n\t\t.delete(schema.userTrustedDevices)\n\t\t.where(and(eq(schema.userTrustedDevices.id, deviceId), eq(schema.userTrustedDevices.userId, userId)))\n\t\t.returning({ id: schema.userTrustedDevices.id });\n\n\tif (result.length === 0) {\n\t\treturn problems.notFound(c, 'Device not found');\n\t}\n\n\tlogSecurityEvent('2fa_device_revoked', 'medium', { userId, deviceId });\n\n\treturn c.json({ success: true });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { generateBackupCodes, hashBackupCode, formatBackupCode } from '../../core/2fa';\nimport { logSecurityEvent } from '../../lib/logger';\nimport { problems } from '../../lib/problem-json';\nimport {\n\tdisableRequestSchema,\n\tregenerateBackupCodesResponseSchema,\n\terrorResponseSchema,\n} from './schemas';\nimport { verifyAny2faCode } from './helpers';\n\nexport const backupCodesRegenerateRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/backup-codes/regenerate',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Regenerate backup codes',\n\tdescription:\n\t\t'Generate new set of backup codes. Requires 2FA verification. All existing codes are invalidated.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: disableRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Backup codes regenerated successfully',\n\t\t\tcontent: { 'application/json': { schema: regenerateBackupCodesResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid verification code',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t401: {\n\t\t\tdescription: 'Unauthorized',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const backupCodesRegenerateHandler = async (c: any) => {\n\tconst userId = c.get('userId');\n\tif (!userId) return problems.unauthorized(c);\n\n\tconst { code, method } = c.req.valid('json');\n\tconst { db, schema, env } = getAuthContext(c);\n\n\tconst verification = await verifyAny2faCode(db, env, userId, code, method, {\n\t\tuser2faMethods: schema.user2faMethods,\n\t\tuserBackupCodes: schema.userBackupCodes,\n\t});\n\tif (!verification.valid) {\n\t\tlogSecurityEvent('2fa_backup_regenerate_failed', 'medium', { userId });\n\t\treturn problems.badRequest(c, 'Invalid verification code');\n\t}\n\n\tawait db.delete(schema.userBackupCodes).where(eq(schema.userBackupCodes.userId, userId));\n\n\tconst codes = generateBackupCodes();\n\tconst now = Date.now();\n\tconst hashedCodes = await Promise.all(\n\t\tcodes.map(async (code) => ({\n\t\t\tuserId,\n\t\t\tcodeHash: await hashBackupCode(code),\n\t\t\tcreatedAt: now,\n\t\t}))\n\t);\n\n\tawait db.insert(schema.userBackupCodes).values(hashedCodes);\n\n\tlogSecurityEvent('2fa_backup_codes_regenerated', 'high', { userId });\n\n\treturn c.json({ success: true, backupCodes: codes.map(formatBackupCode) });\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { getCookie } from 'hono/cookie';\nimport {\n\tvalidateChallengePayload,\n\tretrieveChallengeToken,\n\tupdateChallengeAttempts,\n\tdeleteChallengeToken,\n\tMAX_CHALLENGE_ATTEMPTS,\n\tparseDeviceName,\n\tparseDeviceType,\n\tcreateDeviceToken,\n} from '../../core/2fa';\nimport {\n\tgetChallengeCookieName,\n\tclearChallengeCookie,\n\tsetTrustedDeviceCookie,\n\tsetSessionCookie,\n} from '../../core/cookies';\nimport { hashToken } from '../../core/tokens';\nimport { generateFingerprint, getClientIp } from '../../core/fingerprint';\nimport { createSession } from '../../core/session';\nimport logger, { logSecurityEvent } from '../../lib/logger';\nimport { problems } from '../../lib/problem-json';\nimport { challengeRequestSchema, challengeResponseSchema, errorResponseSchema } from './schemas';\nimport { verifyAny2faCode } from './helpers';\n\nexport const challengeRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/challenge',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Verify 2FA challenge',\n\tdescription: 'Complete 2FA verification during login. Optionally mark device as trusted.',\n\trequest: {\n\t\tbody: {\n\t\t\tcontent: { 'application/json': { schema: challengeRequestSchema } },\n\t\t\trequired: true,\n\t\t},\n\t},\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: '2FA challenge completed successfully',\n\t\t\tcontent: { 'application/json': { schema: challengeResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Invalid code or challenge expired',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'User not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const challengeHandler = async (c: any) => {\n\tconst { code, method, rememberDevice } = c.req.valid('json');\n\tconst { db, schema } = getAuthContext(c);\n\n\tconst cookieName = getChallengeCookieName(c.env);\n\tconst challengeToken = getCookie(c, cookieName);\n\n\tif (!challengeToken) {\n\t\treturn problems.badRequest(c, 'Challenge expired. Please log in again.');\n\t}\n\n\tconst payload = await retrieveChallengeToken(c.env.OAUTH_STATES, challengeToken);\n\tif (!payload) {\n\t\tclearChallengeCookie(c, c.env);\n\t\treturn problems.badRequest(c, 'Challenge expired. Please log in again.');\n\t}\n\n\tconst validation = validateChallengePayload(payload);\n\tif (!validation.valid) {\n\t\tawait deleteChallengeToken(c.env.OAUTH_STATES, challengeToken);\n\t\tclearChallengeCookie(c, c.env);\n\t\tconst message =\n\t\t\tvalidation.reason === 'expired'\n\t\t\t\t? 'Challenge expired. Please log in again.'\n\t\t\t\t: 'Too many failed attempts. Please log in again.';\n\t\tlogSecurityEvent('2fa_challenge_invalidated', 'medium', {\n\t\t\tuserId: payload.userId,\n\t\t\treason: validation.reason,\n\t\t});\n\t\treturn problems.badRequest(c, message);\n\t}\n\n\tconst verification = await verifyAny2faCode(db, c.env, payload.userId, code, method, {\n\t\tuser2faMethods: schema.user2faMethods,\n\t\tuserBackupCodes: schema.userBackupCodes,\n\t});\n\n\tif (!verification.valid) {\n\t\tpayload.attempts++;\n\t\tawait updateChallengeAttempts(c.env.OAUTH_STATES, challengeToken, payload);\n\n\t\tconst remaining = MAX_CHALLENGE_ATTEMPTS - payload.attempts;\n\t\tlogSecurityEvent('2fa_challenge_failed', 'medium', {\n\t\t\tuserId: payload.userId,\n\t\t\tattempts: payload.attempts,\n\t\t});\n\n\t\treturn problems.badRequest(\n\t\t\tc,\n\t\t\t`Invalid code. ${remaining} attempt${remaining === 1 ? '' : 's'} remaining.`\n\t\t);\n\t}\n\n\tawait deleteChallengeToken(c.env.OAUTH_STATES, challengeToken);\n\tclearChallengeCookie(c, c.env);\n\n\tconst [user] = await db.select().from(schema.users).where(eq(schema.users.id, payload.userId)).limit(1);\n\n\tif (!user) {\n\t\tlogger.error('User not found during 2FA challenge', { userId: payload.userId });\n\t\treturn problems.notFound(c, 'User not found');\n\t}\n\n\tconst fingerprint = await generateFingerprint(c.req.raw);\n\tconst ipAddress = getClientIp(c.req.raw);\n\tconst sessionId = await createSession(db, { sessions: schema.sessions }, user.id, fingerprint, ipAddress);\n\n\t// Create trusted device if requested\n\tif (rememberDevice) {\n\t\tconst { token, expiresAt } = createDeviceToken();\n\t\tconst deviceTokenHash = await hashToken(token);\n\t\tconst userAgent = c.req.header('User-Agent') || '';\n\t\tconst deviceName = parseDeviceName(userAgent);\n\t\tconst deviceType = parseDeviceType(userAgent);\n\t\tconst now = Date.now();\n\n\t\tawait db.insert(schema.userTrustedDevices).values({\n\t\t\tuserId: user.id,\n\t\t\ttokenHash: deviceTokenHash,\n\t\t\tdeviceName,\n\t\t\tdeviceType,\n\t\t\tipAddress,\n\t\t\texpiresAt,\n\t\t\tlastUsedAt: now,\n\t\t\tcreatedAt: now,\n\t\t});\n\n\t\tsetTrustedDeviceCookie(c, c.env, token);\n\t\tlogSecurityEvent('2fa_device_trusted', 'low', { userId: user.id, deviceName });\n\t}\n\n\tlogSecurityEvent('2fa_challenge_success', 'low', { userId: user.id, method });\n\n\tc.header('Set-Cookie', setSessionCookie(sessionId, c.env), { append: true });\n\treturn c.json({\n\t\tuser: {\n\t\t\tid: user.id,\n\t\t\temail: user.email,\n\t\t\tname: user.name,\n\t\t},\n\t});\n};\n","import { createRoute } from '@hono/zod-openapi';\nimport { eq } from 'drizzle-orm';\nimport { getAuthContext } from '../../factory';\nimport { getCookie } from 'hono/cookie';\nimport { retrieveChallengeToken } from '../../core/2fa';\nimport { getChallengeCookieName, clearChallengeCookie } from '../../core/cookies';\nimport { hashToken } from '../../core/tokens';\nimport logger from '../../lib/logger';\nimport { EmailService } from '../../lib/email';\nimport { problems } from '../../lib/problem-json';\nimport { resendResponseSchema, errorResponseSchema } from './schemas';\nimport { generateEmailOtp } from './helpers';\n\nexport const challengeResendRoute = createRoute({\n\tmethod: 'post',\n\tpath: '/challenge/resend',\n\ttags: ['Two-Factor Authentication'],\n\tsummary: 'Resend 2FA challenge code',\n\tdescription: 'Resend email verification code during 2FA challenge',\n\tresponses: {\n\t\t200: {\n\t\t\tdescription: 'Code resent successfully',\n\t\t\tcontent: { 'application/json': { schema: resendResponseSchema } },\n\t\t},\n\t\t400: {\n\t\t\tdescription: 'Challenge expired or email verification not available',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t\t404: {\n\t\t\tdescription: 'User not found',\n\t\t\tcontent: { 'application/json': { schema: errorResponseSchema } },\n\t\t},\n\t},\n});\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- OpenAPI handler typing limitation\nexport const challengeResendHandler = async (c: any) => {\n\tconst { db, schema } = getAuthContext(c);\n\n\tconst cookieName = getChallengeCookieName(c.env);\n\tconst challengeToken = getCookie(c, cookieName);\n\n\tif (!challengeToken) {\n\t\treturn problems.badRequest(c, 'Challenge expired. Please log in again.');\n\t}\n\n\tconst payload = await retrieveChallengeToken(c.env.OAUTH_STATES, challengeToken);\n\tif (!payload) {\n\t\tclearChallengeCookie(c, c.env);\n\t\treturn problems.badRequest(c, 'Challenge expired. Please log in again.');\n\t}\n\n\tif (!payload.methods.includes('email')) {\n\t\treturn problems.badRequest(c, 'Email verification not available');\n\t}\n\n\tconst [user] = await db\n\t\t.select({ email: schema.users.email, name: schema.users.name })\n\t\t.from(schema.users)\n\t\t.where(eq(schema.users.id, payload.userId))\n\t\t.limit(1);\n\n\tif (!user) {\n\t\tlogger.error('User not found during 2FA resend', { userId: payload.userId });\n\t\treturn problems.notFound(c, 'User not found');\n\t}\n\n\tconst code = generateEmailOtp();\n\tconst codeHash = await hashToken(code);\n\n\t// 5 minute TTL\n\tawait c.env.OAUTH_STATES.put(`email_2fa_challenge:${payload.userId}`, codeHash, {\n\t\texpirationTtl: 300,\n\t});\n\n\tconst emailService = new EmailService(c.env);\n\tconst firstName = user.name?.split(' ')[0] || null;\n\tawait emailService.send2faCodeEmail({ email: user.email, firstName, code }, db);\n\n\tlogger.info('Email 2FA code resent', { userId: payload.userId });\n\n\treturn c.json({ success: true });\n};\n","import { Webhook } from 'svix';\nimport logger from '../logger';\n\n/**\n * Verify Resend webhook signature using svix\n * Resend uses svix for webhook signing\n */\nexport function verifyWebhookSignature(\n\tpayload: string,\n\tsignature: string,\n\tsecret: string\n): boolean {\n\tif (!secret || secret.trim() === '') {\n\t\tthrow new Error('Webhook secret is required');\n\t}\n\n\ttry {\n\t\tconst wh = new Webhook(secret);\n\t\t// Svix verify method throws if signature is invalid\n\t\twh.verify(payload, {\n\t\t\t'svix-id': '', // Not used by Resend\n\t\t\t'svix-timestamp': '', // Not used by Resend\n\t\t\t'svix-signature': signature,\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tlogger.warn('Webhook signature verification failed', {\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t});\n\t\treturn false;\n\t}\n}\n"],"mappings":"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","names":["tables","bcrypt","eq","and","gt","isProduction","eq","tables","eq","eq","tables","eq","eq","and","gt","auth","and","eq","gt","session","createMiddleware","createMiddleware","createMiddleware","createMiddleware","createMiddleware","eq","createMiddleware","eq","OpenAPIHono","eq","eq","eq","Resend","eq","tables","eq","sql","sql","existingUser","eq","hashedPassword","passwordValidation","newUser","fingerprint","ipAddress","sessionId","createRoute","eq","eq","and","eq","tables","and","createRoute","eq","fingerprint","ipAddress","sessionId","createRoute","eq","createRoute","eq","createRoute","eq","createRoute","eq","createRoute","eq","createRoute","eq","createRoute","eq","and","gt","sql","tables","eq","and","gt","sql","createRoute","createRoute","createRoute","createRoute","eq","and","tables","eq","and","createRoute","createRoute","createRoute","createRoute","z","z","createRoute","createRoute","z","z","createRoute","createRoute","z","z","createRoute","createRoute","z","eq","z","createRoute","eq","createRoute","z","eq","and","gt","z","createRoute","and","eq","gt","createRoute","z","eq","z","errorResponseSchema","createRoute","eq","createRoute","eq","and","isNull","z","errorResponseSchema","createRoute","errorResponseSchema","and","eq","isNull","createRoute","eq","and","createRoute","errorResponseSchema","and","eq","createRoute","eq","createRoute","errorResponseSchema","eq","code","createRoute","eq","and","createRoute","errorResponseSchema","and","eq","createRoute","eq","and","createRoute","errorResponseSchema","and","eq","createRoute","eq","createRoute","errorResponseSchema","eq","code","createRoute","eq","and","createRoute","errorResponseSchema","and","eq","createRoute","eq","and","createRoute","errorResponseSchema","and","eq","createRoute","z","eq","and","gt","createRoute","errorResponseSchema","and","eq","gt","z","createRoute","eq","createRoute","errorResponseSchema","eq","code","createRoute","eq","getCookie","createRoute","errorResponseSchema","getCookie","eq","createRoute","eq","getCookie","createRoute","errorResponseSchema","getCookie","eq","OpenAPIHono"]}
|