67 lines
1.7 KiB
JavaScript
67 lines
1.7 KiB
JavaScript
import rateLimit from 'express-rate-limit';
|
|
import helmet from 'helmet';
|
|
import csrf from 'csurf';
|
|
import config from '../config.js';
|
|
|
|
// Rate limiter for general API requests
|
|
export const apiLimiter = rateLimit({
|
|
windowMs: config.rateLimit.windowMs,
|
|
max: config.rateLimit.maxRequests,
|
|
message: 'Too many requests, please try again later',
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
});
|
|
|
|
// Stricter rate limiter for auth endpoints
|
|
export const authLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 5,
|
|
message: 'Too many login attempts, please try again later',
|
|
skipSuccessfulRequests: true,
|
|
});
|
|
|
|
// Security headers middleware
|
|
export function securityHeaders(req, res, next) {
|
|
helmet()(req, res, next);
|
|
}
|
|
|
|
// CSRF protection
|
|
export const csrfProtection = csrf({ cookie: false });
|
|
|
|
// Request logging
|
|
export function requestLogger(req, res, next) {
|
|
const start = Date.now();
|
|
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - start;
|
|
console.log(
|
|
`[${new Date().toISOString()}] ${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`
|
|
);
|
|
});
|
|
|
|
next();
|
|
}
|
|
|
|
// Error handler middleware
|
|
export function errorHandler(err, req, res, next) {
|
|
console.error('Error:', err);
|
|
|
|
if (err.code === 'EBADCSRFTOKEN') {
|
|
return res.status(403).json({ error: 'Invalid CSRF token' });
|
|
}
|
|
|
|
if (err.message && err.message.includes('OIDC')) {
|
|
return res.status(401).json({ error: 'Authentication failed' });
|
|
}
|
|
|
|
res.status(err.status || 500).json({
|
|
error: err.message || 'Internal server error',
|
|
});
|
|
}
|
|
|
|
// Middleware to attach CSRF token to response
|
|
export function attachCsrfToken(req, res, next) {
|
|
res.locals.csrfToken = req.csrfToken();
|
|
next();
|
|
}
|