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