const rateLimit = require('express-rate-limit'); const zod = require('zod'); const { AppError } = require('../utils/errors'); const { register, verifyLogin } = require('../services/authService'); // Validation schemas const registerSchema = zod.strictObject({ username: zod.string().min(3).max(20).regex(/^[A-Za-z0-9_]+$/), password: zod.string().min(8).max(72), }); const loginSchema = zod.strictObject({ username: zod.string().min(1).max(20), password: zod.string().min(1).max(72), }); // Rate limiters — temporarily disabled for debugging // const registerLimiter = rateLimit({ // windowMs: 60 * 60 * 1000, // 1 hour // max: process.env.NODE_ENV === 'test' ? 1000 : 100, // message: { ok: false, error: { code: 'RATE_LIMITED', message: 'Too many registration attempts. Try again later.' } }, // }); // const loginLimiter = rateLimit({ // windowMs: 15 * 60 * 1000, // 15 minutes // max: process.env.NODE_ENV === 'test' ? 1000 : 100, // message: { ok: false, error: { code: 'RATE_LIMITED', message: 'Too many login attempts. Try again later.' } }, // }); const registerLimiter = (_req, _res, next) => next(); const loginLimiter = (_req, _res, next) => next(); function wrap(fn) { return (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); } async function handleRegister(req, res) { const parsed = registerSchema.safeParse(req.body); if (!parsed.success) { throw new AppError('VALIDATION_ERROR', 'Request validation failed.', 400); } const { username, password } = parsed.data; const user = await register(username, password); // Regenerate session to prevent fixation req.session.regenerate((err) => { if (err) throw new AppError('INTERNAL_ERROR', 'Session regeneration failed.', 500); req.session.userId = user.id; res.status(201).json({ ok: true, data: { user: { id: user.id, username: user.username } } }); }); } async function handleLogin(req, res) { const parsed = loginSchema.safeParse(req.body); if (!parsed.success) { throw new AppError('VALIDATION_ERROR', 'Request validation failed.', 400); } const { username, password } = parsed.data; const user = await verifyLogin(username, password); // Regenerate session to prevent fixation req.session.regenerate((err) => { if (err) throw new AppError('INTERNAL_ERROR', 'Session regeneration failed.', 500); req.session.userId = user.id; res.json({ ok: true, data: { user: { id: user.id, username: user.username }, hasHero: false } }); }); } async function handleLogout(req, res) { req.session.destroy((err) => { if (err) throw new AppError('INTERNAL_ERROR', 'Logout failed.', 500); res.clearCookie('emberfall_session'); res.json({ ok: true, data: {} }); }); } async function handleMe(req, res) { const userId = req.session.userId; res.json({ ok: true, data: { user: { id: userId, username: req.session.username || '' }, hasHero: false } }); } module.exports = { registerLimiter, loginLimiter, routes: { postRegister: wrap(handleRegister), postLogin: wrap(handleLogin), postLogout: wrap(handleLogout), getMe: wrap(handleMe), }, };