Skip to main content

csrf

Type: RequestHandler Access: nauth.middleware.csrf

Express middleware that validates CSRF tokens for state-changing requests when using cookie-based token delivery.

Signature

nauth.middleware.csrf: RequestHandler

Overview

The csrf middleware validates CSRF tokens to prevent Cross-Site Request Forgery attacks. It uses lazy validation - errors are stored in request attributes and only thrown when requireAuth() is called.

Key Features:

  • Validates CSRF token from header matches cookie value
  • Skips safe HTTP methods (GET, HEAD, OPTIONS)
  • Skips routes marked with public() helper
  • Deferred validation until requireAuth() is called
  • Only enforces for cookie-based token delivery

Usage

Mount Order

CSRF middleware must be mounted after clientInfo and before auth:

import express from 'express';
import { NAuth, ExpressAdapter } from '@nauth-toolkit/core';

const app = express();
const nauth = await NAuth.create({
config: authConfig,
dataSource,
adapter: new ExpressAdapter(),
});

// Mount order matters!
app.use(nauth.middleware.clientInfo); // 1st - Initializes context
app.use(nauth.middleware.csrf); // 2nd - CSRF validation
app.use(nauth.middleware.auth); // 3rd - JWT validation
app.use(nauth.middleware.tokenDelivery); // 4th - Token delivery

Configuration

CSRF middleware is automatically enabled when using cookie-based token delivery:

const nauth = await NAuth.create({
config: {
tokenDelivery: {
method: 'cookies', // CSRF middleware enabled
},
security: {
csrf: {
// Name of the CSRF header to check. Default: 'x-csrf-token'
headerName: 'x-csrf-token',
// Name of the CSRF cookie. Default: '<prefix>csrf_token'
cookieName: 'nauth_csrf_token',
// Length of CSRF token in bytes. Default: 32
tokenLength: 32,
// Paths that bypass CSRF validation entirely (prefix match)
excludedPaths: ['/webhook', '/public-api'],
// Cookie options for the CSRF token cookie
cookieOptions: {
secure: true, // HTTPS only. Set false for localhost
sameSite: 'strict', // 'strict' | 'lax' | 'none'
domain: undefined, // e.g. '.example.com' for subdomain sharing
path: '/',
priority: 'high', // 'low' | 'medium' | 'high'
},
},
},
},
dataSource,
adapter: new ExpressAdapter(),
});
note

excludedPaths uses a prefix match (String.prototype.startsWith). A path of /webhook will match /webhook, /webhook/stripe, etc.

Behavior

  • Validates x-csrf-token header against cookie value
  • Skips GET, HEAD, OPTIONS requests (safe methods)
  • Skips routes marked with public() helper
  • Stores errors in req.attributes.nauthCsrfError (lazy validation)
  • Only enforces for cookie-based token delivery

Frontend Integration

Frontend must send CSRF token in headers:

// Read CSRF token from cookie
const csrfToken = getCookie('nauth_csrf_token');

// Send in header for POST/PUT/DELETE/PATCH
fetch('/api/action', {
method: 'POST',
headers: {
'x-csrf-token': csrfToken,
},
body: JSON.stringify({ data: 'value' }),
});

Errors

CSRF errors are stored in request attributes and thrown by requireAuth():

CodeWhenDetails
CSRF_TOKEN_MISSINGToken missing in header or cookieundefined
CSRF_TOKEN_INVALIDToken mismatchundefined