Configuration: - app.config.js: Centralized configuration (ports, MongoDB, JWT, features) - Feature flags for AI curation, media triage, case submissions Middleware: - auth.middleware.js: JWT authentication, role-based access control - validation.middleware.js: Input validation, sanitization, ObjectId checks - error.middleware.js: Global error handling, async wrapper, 404 handler Express Server (src/server.js): - Security: Helmet, CORS, rate limiting - Request logging with Winston - Health check endpoint - MongoDB connection with graceful shutdown - Static file serving - Temporary homepage showing development status Features: - Production-ready error handling - MongoDB duplicate key detection - JWT token validation - XSS protection via sanitization - Rate limiting (100 req / 15min per IP) - Graceful shutdown (SIGTERM/SIGINT) Status: Server foundation complete, ready for API routes Port: 9000 Database: tractatus_dev (MongoDB 27017)
109 lines
2.3 KiB
JavaScript
109 lines
2.3 KiB
JavaScript
/**
|
|
* Authentication Middleware
|
|
* JWT-based authentication for admin routes
|
|
*/
|
|
|
|
const { verifyToken, extractTokenFromHeader } = require('../utils/jwt.util');
|
|
const { User } = require('../models');
|
|
const logger = require('../utils/logger.util');
|
|
|
|
/**
|
|
* Verify JWT token and attach user to request
|
|
*/
|
|
async function authenticateToken(req, res, next) {
|
|
try {
|
|
const token = extractTokenFromHeader(req.headers.authorization);
|
|
|
|
if (!token) {
|
|
return res.status(401).json({
|
|
error: 'Authentication required',
|
|
message: 'No token provided'
|
|
});
|
|
}
|
|
|
|
// Verify token
|
|
const decoded = verifyToken(token);
|
|
|
|
// Get user from database
|
|
const user = await User.findById(decoded.userId);
|
|
|
|
if (!user) {
|
|
return res.status(401).json({
|
|
error: 'Authentication failed',
|
|
message: 'User not found'
|
|
});
|
|
}
|
|
|
|
if (!user.active) {
|
|
return res.status(401).json({
|
|
error: 'Authentication failed',
|
|
message: 'User account is inactive'
|
|
});
|
|
}
|
|
|
|
// Attach user to request
|
|
req.user = user;
|
|
req.userId = user._id;
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error('Authentication error:', error);
|
|
|
|
return res.status(401).json({
|
|
error: 'Authentication failed',
|
|
message: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if user has required role
|
|
*/
|
|
function requireRole(...roles) {
|
|
return (req, res, next) => {
|
|
if (!req.user) {
|
|
return res.status(401).json({
|
|
error: 'Authentication required'
|
|
});
|
|
}
|
|
|
|
if (!roles.includes(req.user.role)) {
|
|
return res.status(403).json({
|
|
error: 'Insufficient permissions',
|
|
message: `Required role: ${roles.join(' or ')}`
|
|
});
|
|
}
|
|
|
|
next();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Optional authentication (attach user if token present, continue if not)
|
|
*/
|
|
async function optionalAuth(req, res, next) {
|
|
try {
|
|
const token = extractTokenFromHeader(req.headers.authorization);
|
|
|
|
if (token) {
|
|
const decoded = verifyToken(token);
|
|
const user = await User.findById(decoded.userId);
|
|
|
|
if (user && user.active) {
|
|
req.user = user;
|
|
req.userId = user._id;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// Silently fail - authentication is optional
|
|
logger.debug('Optional auth failed:', error.message);
|
|
}
|
|
|
|
next();
|
|
}
|
|
|
|
module.exports = {
|
|
authenticateToken,
|
|
requireRole,
|
|
optionalAuth
|
|
};
|