File size: 1,475 Bytes
d53e5b4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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 });
};
|