From 0044f1e965bd45910cba2156c378091b7372f8ac Mon Sep 17 00:00:00 2001 From: FragginWagon Date: Wed, 28 Jan 2026 22:46:10 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20Add=20authentication=20middlewar?= =?UTF-8?q?e=20for=20secure=20access?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pokedex.online/server/middleware/auth.js | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 code/websites/pokedex.online/server/middleware/auth.js diff --git a/code/websites/pokedex.online/server/middleware/auth.js b/code/websites/pokedex.online/server/middleware/auth.js new file mode 100644 index 0000000..9aacf62 --- /dev/null +++ b/code/websites/pokedex.online/server/middleware/auth.js @@ -0,0 +1,122 @@ +/** + * 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); +}