import { Hono } from 'hono'; import { jsonValidator } from '../../shared/validation/index.js'; import { rateLimiter } from 'hono-rate-limiter'; import { env } from '../../config/index.js'; import { getClientIp } from '../../lib/clientIp.js'; import { created, success } from '../../lib/response.js'; import { requireAuth } from '../../middleware/auth.js'; import { loginSchema, registerSchema } from './auth.schemas.js'; import { createGuest, login, logout, register, toPublicUser, } from './auth.service.js'; import { prisma } from '../../lib/prisma.js'; import { AUDIT_ACTIONS } from '../admin/auditActions.js'; import { recordAuditBestEffort } from '../admin/audit.service.js'; import { UserRole } from '@prisma/client'; const authAbuseLimiter = rateLimiter({ windowMs: env.RATE_LIMIT_WINDOW_MS, limit: env.AUTH_RATE_LIMIT_MAX, standardHeaders: 'draft-6', keyGenerator: (c) => `auth:${getClientIp(c)}`, handler: (c) => c.json( { success: false, error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests', }, meta: { requestId: c.get('requestId') ?? 'unknown', timestamp: new Date().toISOString(), }, }, 429, ), }); export function createAuthRoutes() { const routes = new Hono(); routes.post('/register', authAbuseLimiter, jsonValidator(registerSchema), async (c) => { const { email, password } = c.req.valid('json'); const result = await register(email, password); return created(c, result); }); routes.post('/login', authAbuseLimiter, jsonValidator(loginSchema), async (c) => { const { email, password } = c.req.valid('json'); const result = await login(email, password); if (result.user.role === UserRole.admin) { const user = await prisma.user.findUnique({ where: { externalId: result.user.id }, select: { id: true }, }); await recordAuditBestEffort({ actorUserId: user?.id ?? null, action: AUDIT_ACTIONS.ADMIN_LOGIN, metadata: { source: 'password_login' }, ipAddress: getClientIp(c), userAgent: c.req.header('user-agent') ?? null, }); } return success(c, result); }); routes.post('/guest', authAbuseLimiter, async (c) => { const result = await createGuest(); return created(c, result); }); routes.get('/me', requireAuth, async (c) => { const authUser = c.get('authUser'); const user = await prisma.user.findUnique({ where: { id: authUser.id }, include: { profile: true, identities: true }, }); if (!user) { return success(c, { id: authUser.externalId, email: authUser.email, isGuest: authUser.isGuest, profileComplete: false, role: authUser.role, }); } return success(c, toPublicUser(user)); }); routes.post('/logout', requireAuth, async (c) => { const authUser = c.get('authUser'); await logout(authUser.sessionId); return success(c, { loggedOut: true }); }); return routes; }