import { Request, Response, NextFunction } from 'express'; import crypto from 'crypto'; /** * Middleware to generate a CSRF token for the session / request. * If the request doesn't have a token cookie, one is generated and attached. */ export const csrfProtection = (req: Request, res: Response, next: NextFunction) => { // Determine the token let token = req.cookies['csrf-token']; if (!token) { token = crypto.randomBytes(32).toString('hex'); res.cookie('csrf-token', token, { httpOnly: false, // Must be readable by frontend JS to set header sameSite: 'lax', secure: process.env.NODE_ENV === 'production', path: '/', }); } // Attach to request object for use in endpoints if needed (req as any).csrfToken = token; // Fast-path for GET / OPTIONS / HEAD if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } // For POST/PATCH/DELETE require the token in the headers const clientToken = req.headers['x-csrf-token']; if (!clientToken || clientToken !== token) { return res.status(403).json({ error: 'CSRF token missing or invalid', }); } next(); }; /** * Endpoint to explicitly fetch the CSRF token */ export const getCsrfToken = (req: Request, res: Response) => { res.status(200).json({ csrfToken: (req as any).csrfToken }); };