/** * Authentication Middleware * * Middleware for authenticating and authorizing requests using JWT tokens */ import { verifyToken } from '../utils/jwt-utils.js'; /** * Middleware to verify JWT token from Authorization header * Sets req.user if token is valid * * @param {Object} options - Configuration options * @param {string} options.secret - JWT secret for token verification * @param {boolean} options.optional - If true, missing token is not an error (default: false) * @returns {Function} Express middleware function */ export function authMiddleware({ secret, optional = false } = {}) { return (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader) { if (optional) { req.user = null; return next(); } return res.status(401).json({ error: 'Missing authorization header', code: 'MISSING_AUTH_HEADER' }); } // Extract token from "Bearer " format const parts = authHeader.split(' '); if (parts.length !== 2 || parts[0] !== 'Bearer') { return res.status(401).json({ error: 'Invalid authorization header format. Expected: Bearer ', code: 'INVALID_AUTH_FORMAT' }); } const token = parts[1]; try { const decoded = verifyToken(token, secret); req.user = decoded; next(); } catch (err) { const code = err.message.includes('expired') ? 'TOKEN_EXPIRED' : 'INVALID_TOKEN'; return res.status(401).json({ error: err.message, code }); } }; } /** * Middleware to check if user has required permissions * * @param {string|string[]} requiredPermissions - Permission or array of permissions required * @returns {Function} Express middleware function */ export function requirePermission(requiredPermissions) { const permissions = Array.isArray(requiredPermissions) ? requiredPermissions : [requiredPermissions]; return (req, res, next) => { if (!req.user) { return res.status(403).json({ error: 'Forbidden: User not authenticated', code: 'NOT_AUTHENTICATED' }); } const userPermissions = req.user.permissions || []; const hasPermission = permissions.some(perm => userPermissions.includes(perm) ); if (!hasPermission) { return res.status(403).json({ error: `Forbidden: Missing required permissions: ${permissions.join(', ')}`, code: 'INSUFFICIENT_PERMISSIONS' }); } next(); }; } /** * Middleware to check if user is admin * * @returns {Function} Express middleware function */ export function requireAdmin(req, res, next) { if (!req.user || !req.user.isAdmin) { return res.status(403).json({ error: 'Forbidden: Admin access required', code: 'ADMIN_REQUIRED' }); } next(); } /** * Error handler for authentication errors * * @param {Error} err - Error object * @param {Object} req - Express request * @param {Object} res - Express response * @param {Function} next - Express next function */ export function authErrorHandler(err, req, res, next) { if (err.code === 'INVALID_TOKEN' || err.code === 'TOKEN_EXPIRED') { return res.status(401).json({ error: err.message, code: err.code }); } next(err); }