88 lines
2.4 KiB
JavaScript
88 lines
2.4 KiB
JavaScript
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 });
|
|
}
|