const router = require('express').Router(); const auth = require('../core/auth.core'); const jwt = require('../tools/jwt'); const CONSOLE_URL = process.env.CONSOLE_URL || 'http://localhost:3004'; const COOKIE_DOMAIN = process.env.COOKIE_DOMAIN || undefined; // Validazione input const USERNAME_REGEX = /^[a-zA-Z0-9_.\-]{3,50}$/; const PASSWORD_MIN_LENGTH = 8; const PASSWORD_MAX_LENGTH = 128; router.post('/register', async (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ error: 'Username e password richiesti' }); } if (typeof username !== 'string' || typeof password !== 'string') { return res.status(400).json({ error: 'Formato dati non valido' }); } if (!USERNAME_REGEX.test(username)) { return res.status(400).json({ error: 'Username non valido. 3-50 caratteri alfanumerici, underscore, punto o trattino.' }); } if (password.length < PASSWORD_MIN_LENGTH || password.length > PASSWORD_MAX_LENGTH) { return res.status(400).json({ error: `Password deve essere tra ${PASSWORD_MIN_LENGTH} e ${PASSWORD_MAX_LENGTH} caratteri` }); } try { await auth.register(username, password); res.status(201).end(); } catch (err) { console.error('[AUTH] Register failed:', err.message); const status = err.message === 'User already exists' ? 409 : 500; res.status(status).json({ error: err.message === 'User already exists' ? err.message : 'Errore interno' }); } }); router.post('/login', async (req, res) => { const { username, password, redirect, _csrf } = req.body; const loginRedirect = (errorKey, safeRedirect) => { const params = new URLSearchParams({ error: errorKey }); if (safeRedirect) params.set('redirect', safeRedirect); return res.redirect(`/login?${params.toString()}`); }; // Validazione CSRF (double-submit cookie) const csrfCookie = req.cookies && req.cookies._csrf; if (!_csrf || !csrfCookie || _csrf !== csrfCookie) { return loginRedirect('csrf', ''); } // Validazione base if (!username || !password || typeof username !== 'string' || typeof password !== 'string') { return loginRedirect('invalid_credentials', redirect || ''); } // Limiti di lunghezza per prevenire abuse if (username.length > 50 || password.length > PASSWORD_MAX_LENGTH) { return loginRedirect('invalid_credentials', redirect || ''); } // Validazione redirect URL per prevenire open redirect attacks let safeRedirect = ''; if (redirect && typeof redirect === 'string') { try { const redirectUrl = new URL(redirect); const consoleUrl = new URL(CONSOLE_URL); if (redirectUrl.hostname !== consoleUrl.hostname) { return loginRedirect('invalid_redirect', ''); } safeRedirect = redirect; } catch { // URL relativo o non valido — ignora il redirect } } try { console.log('[DEBUG ROUTES] POST /api/auth/login START - username:', username);\n const user = await auth.login(username, password);\n console.log('[DEBUG ROUTES] auth.login() success - user:', user); const session = await auth.newSession(user.id, req.headers['user-agent'], req.ip);\n console.log('[DEBUG ROUTES] auth.newSession() success - session:', session); const token = jwt.generateToken(user, session.id);\n console.log('[DEBUG ROUTES] jwt.generateToken() success'); const cookieOptions = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 7 * 24 * 60 * 60 * 1000 }; if (COOKIE_DOMAIN) { cookieOptions.domain = COOKIE_DOMAIN; } res.cookie('auth_token', token, cookieOptions); res.clearCookie('_csrf'); console.log('[DEBUG ROUTES] cookies set - redirecting to:', safeRedirect || CONSOLE_URL); const destination = safeRedirect || CONSOLE_URL; res.redirect(destination); } catch (err) { console.error('[DEBUG ROUTES] Login FAILED:', err.message, err.code, err);\n return loginRedirect('invalid_credentials', safeRedirect); } }); router.post('/logout', async (req, res) => { const token = req.cookies && req.cookies.auth_token; if (token) { try { const verified = jwt.verifyToken(token); if (verified.valid) { await auth.logout(verified.payload.session_id); } } catch (err) { console.error('[AUTH] Logout error:', err.message); } } const clearOptions = { httpOnly: true, sameSite: 'lax' }; if (COOKIE_DOMAIN) { clearOptions.domain = COOKIE_DOMAIN; } res.clearCookie('auth_token', clearOptions); res.redirect('/login'); }); module.exports = router;