import { Router, Request, Response } from 'express'; import { Pool } from 'pg'; import { AuthService } from './auth.service.ts'; export function createAuthRouter(db: Pool, authService: AuthService): Router { const router = Router(); /** * POST /api/auth/register */ router.post('/register', async (req: Request, res: Response): Promise => { try { const { username, password } = req.body; if (!username || !password || username.trim() === '' || password.length < 8) { res.status(400).json({ success: false, error: 'Username required, and password must be at least 8 characters long.' }); return; } // Check if user already exists const checkUser = await db.query('SELECT id FROM users WHERE username = $1;', [username]); if (checkUser.rows.length > 0) { res.status(409).json({ success: false, error: 'Username is already taken.' }); return; } // Hash password using Argon2id const hash = await authService.hashPassword(password); // Save user await db.query('INSERT INTO users (username, password_hash) VALUES ($1, $2);', [username, hash]); res.status(201).json({ success: true, message: 'User registered successfully!' }); } catch (error) { console.error('Registration failure:', error); res.status(500).json({ success: false, error: 'Internal Server Error' }); } }); /** * POST /api/auth/login */ router.post('/login', async (req: Request, res: Response): Promise => { try { const { username, password } = req.body; if (!username || !password) { res.status(400).json({ success: false, error: 'Username and password are required.' }); return; } const result = await db.query('SELECT id, password_hash FROM users WHERE username = $1;', [username]); if (result.rows.length === 0) { res.status(401).json({ success: false, error: 'Invalid username or password.' }); return; } const user = result.rows[0]; const validPassword = await authService.verifyPassword(password, user.password_hash); if (!validPassword) { res.status(401).json({ success: false, error: 'Invalid username or password.' }); return; } // Generate a high-entropy session token const sessionToken = authService.generateSessionToken(); const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // Expires in 30 days // Store the session securely in the database await db.query( 'INSERT INTO sessions (id, user_id, expires_at) VALUES ($1, $2, $3);', [sessionToken, user.id, expiresAt] ); // Issue the secure HttpOnly cookie res.cookie('session_token', sessionToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', // Requires HTTPS in production sameSite: 'strict', expires: expiresAt, }); res.json({ success: true, message: 'Logged in successfully!' }); } catch (error) { console.error('Login failure:', error); res.status(500).json({ success: false, error: 'Internal Server Error' }); } }); return router; }