127 lines
3.3 KiB
JavaScript
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);
|
|
}
|