/** * Authentication Routes * * Handles login, logout, token refresh, and user info endpoints */ import { Router } from 'express'; import { createToken, verifyToken, decodeToken, getTokenExpiresIn } from '../utils/jwt-utils.js'; export function createAuthRouter({ secret, adminPassword } = {}) { const router = Router(); /** * POST /auth/login * Login with admin password to receive JWT token */ router.post('/login', (req, res) => { const { password } = req.body; // Validate input if (!password) { return res.status(400).json({ error: 'Password is required', code: 'MISSING_PASSWORD' }); } // Validate password if (password !== adminPassword) { return res.status(401).json({ error: 'Invalid password', code: 'INVALID_PASSWORD' }); } try { // Create token with admin permissions const token = createToken( { isAdmin: true, permissions: ['admin', 'gamemaster-edit'], loginTime: new Date().toISOString() }, secret, 7 * 24 * 60 * 60 // 7 days ); res.json({ success: true, token, expiresIn: 7 * 24 * 60 * 60, user: { isAdmin: true, permissions: ['admin', 'gamemaster-edit'] } }); } catch (err) { res.status(500).json({ error: 'Failed to create token', code: 'TOKEN_CREATION_ERROR' }); } }); /** * POST /auth/verify * Verify that a token is valid */ router.post('/verify', (req, res) => { const { token } = req.body; if (!token) { return res.status(400).json({ error: 'Token is required', code: 'MISSING_TOKEN' }); } try { const decoded = verifyToken(token, secret); const expiresIn = getTokenExpiresIn(token); res.json({ valid: true, user: { isAdmin: decoded.isAdmin, permissions: decoded.permissions }, expiresIn: Math.floor(expiresIn / 1000), expiresAt: new Date(Date.now() + expiresIn) }); } catch (err) { return res.status(401).json({ valid: false, error: err.message, code: 'INVALID_TOKEN' }); } }); /** * POST /auth/refresh * Refresh an existing token */ router.post('/refresh', (req, res) => { const { token } = req.body; if (!token) { return res.status(400).json({ error: 'Token is required', code: 'MISSING_TOKEN' }); } try { const decoded = verifyToken(token, secret); // Create new token with same payload but extended expiration const newToken = createToken( { isAdmin: decoded.isAdmin, permissions: decoded.permissions, loginTime: decoded.loginTime }, secret, 7 * 24 * 60 * 60 // 7 days ); res.json({ success: true, token: newToken, expiresIn: 7 * 24 * 60 * 60 }); } catch (err) { return res.status(401).json({ error: err.message, code: 'INVALID_TOKEN' }); } }); /** * GET /auth/user * Get current user info (requires valid token via middleware) */ router.get('/user', (req, res) => { if (!req.user) { return res.status(401).json({ error: 'Not authenticated', code: 'NOT_AUTHENTICATED' }); } res.json({ user: { isAdmin: req.user.isAdmin, permissions: req.user.permissions, loginTime: req.user.loginTime } }); }); /** * POST /auth/logout * Logout (token is invalidated on client side) */ router.post('/logout', (req, res) => { res.json({ success: true, message: 'Logged out successfully' }); }); return router; }