diff --git a/backend/app.js b/backend/app.js index 7ade690..a91bb9d 100644 --- a/backend/app.js +++ b/backend/app.js @@ -97,10 +97,28 @@ const lusca = require('lusca'); // Pre-check middleware to exempt test/Bearer requests before lusca runs app.use((req, res, next) => { + // Public unauthenticated endpoints that should bypass CSRF + // These are routes that don't require authentication and are used during login/registration + const publicPaths = [ + '/api/login', + '/api/register', + '/api/verify-email', + '/api/version', + '/api/registration-status', + '/api/health', + ]; + + const isPublicPath = publicPaths.some((path) => req.path === path); + const isOidcPath = req.path.startsWith('/api/oidc/'); + const isFeatureFlagsPath = req.path.startsWith('/api/feature-flags'); + // Mark exempt requests so lusca wrapper can skip them if ( process.env.NODE_ENV === 'test' || - req.headers.authorization?.startsWith('Bearer ') + req.headers.authorization?.startsWith('Bearer ') || + isPublicPath || + isOidcPath || + isFeatureFlagsPath ) { req._csrfExempt = true; } diff --git a/backend/middleware/csrf.js b/backend/middleware/csrf.js index 50227b6..90787c7 100644 --- a/backend/middleware/csrf.js +++ b/backend/middleware/csrf.js @@ -1,11 +1,16 @@ -const csurf = require('@dr.pogodin/csurf'); +const lusca = require('lusca'); -const csrfMiddleware = csurf({ - cookie: false, - value: (req) => { - return req.headers['x-csrf-token'] || req.body?._csrf; - }, -}); +const csrfMiddleware = (req, res, next) => { + if (!req.session) { + return res.status(500).json({ error: 'Session not initialized' }); + } + + if (!req.session._csrf) { + req.session._csrf = require('crypto').randomBytes(16).toString('hex'); + } + + next(); +}; const csrfProtection = (req, res, next) => { if ( @@ -16,11 +21,22 @@ const csrfProtection = (req, res, next) => { return next(); } - return csrfMiddleware(req, res, next); + return lusca.csrf({ + header: 'x-csrf-token', + cookie: false, + })(req, res, next); }; const generateToken = (req) => { - return req.csrfToken ? req.csrfToken() : ''; + if (!req.session) { + return ''; + } + + if (!req.session._csrf) { + req.session._csrf = require('crypto').randomBytes(16).toString('hex'); + } + + return req.session._csrf; }; module.exports = {