tractatus/src/middleware/auth.middleware.js
TheFlow 6285adc572 feat: add Express server foundation with middleware
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)
2025-10-06 23:56:12 +13:00

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
};