import { Router } from 'express'; import { clearSessionCookie, currentSession, login, logout, setSessionCookie } from '../auth.js'; import { createAuditEvent } from '../store.js'; export const authRouter = Router(); const loginAttempts = new Map(); const maxAttempts = 5; const windowMs = 15 * 60 * 1000; authRouter.get('/session', (req, res) => { const session = currentSession(req); res.json({ authenticated: Boolean(session), user: session?.user || null }); }); authRouter.post('/login', (req, res) => { const username = String(req.body?.username || ''); const key = loginAttemptKey(req, username); const attempt = currentAttempt(key); if (attempt.count >= maxAttempts) { createAuditEvent({ action: 'login_blocked', targetType: 'session', targetId: username || null, details: { ip: clientIp(req), reason: 'rate_limit' }, }); res.status(429).json({ error: 'Too many login attempts. Try again later.' }); return; } const result = login(username, req.body?.password); if (!result) { recordFailedAttempt(key); createAuditEvent({ action: 'login_failed', targetType: 'session', targetId: username || null, details: { ip: clientIp(req) }, }); res.status(401).json({ error: 'Invalid username or password.' }); return; } loginAttempts.delete(key); setSessionCookie(res, result.session); createAuditEvent({ user: result.user, action: 'login', targetType: 'session', targetId: result.session.id, }); res.json({ user: result.user }); }); authRouter.post('/logout', (req, res) => { const session = currentSession(req); if (session) { createAuditEvent({ user: session.user, action: 'logout', targetType: 'session', targetId: session.sessionId, }); } logout(session?.sessionId); clearSessionCookie(res); res.json({ ok: true }); }); function loginAttemptKey(req, username) { return `${clientIp(req)}:${String(username || '').toLowerCase()}`; } function clientIp(req) { return String(req.ip || req.socket?.remoteAddress || '').replace(/^::ffff:/, ''); } function currentAttempt(key) { const now = Date.now(); const current = loginAttempts.get(key); if (!current || current.resetAt <= now) return { count: 0, resetAt: now + windowMs }; return current; } function recordFailedAttempt(key) { const attempt = currentAttempt(key); loginAttempts.set(key, { count: attempt.count + 1, resetAt: attempt.resetAt }); }