Files
memory-infrastructure-palace/code/websites/pokedex.online/server/middleware/auth.js

127 lines
3.3 KiB
JavaScript

/**
* 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 <token>" format
const parts = authHeader.split(' ');
if (parts.length !== 2 || parts[0] !== 'Bearer') {
return res.status(401).json({
error: 'Invalid authorization header format. Expected: Bearer <token>',
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);
}