🔒 Add authentication middleware for secure access
This commit is contained in:
122
code/websites/pokedex.online/server/middleware/auth.js
Normal file
122
code/websites/pokedex.online/server/middleware/auth.js
Normal file
@@ -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 <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);
|
||||
}
|
||||
Reference in New Issue
Block a user