Skip to content

csrf

Provides CSRF protection using HMAC-signed tokens. Unlike other middleware, csrf() returns an object with two methods — issue and verify — rather than a single middleware function. In ergo-router, the pipeline builder automatically dispatches issue for safe methods (GET, HEAD, OPTIONS) and verify for unsafe methods.

Pipeline stage: Authorization

import {csrf} from '@centralping/ergo';
Option Type Default Description
secret string Required. HMAC secret for token signing
cookieTokenName string 'CSRF-TOKEN' Cookie name for the CSRF token
headerTokenName string 'X-CSRF-TOKEN' Request header name for the token
cookieUuidName string 'CSRF-UUID' Cookie name for the CSRF UUID
encoding string 'base64' Token encoding
cookieOptions object {} Cookie directives (note: httpOnly and sameSite are locked)
  • Token cookie: httpOnly: false (locked — client JS must read it), sameSite: 'Strict' (locked)
  • UUID cookie: httpOnly: true (locked — set after cookieOptions spread), sameSite: 'Strict' (locked)
  • sameSite cannot be overridden via cookieOptions on either cookie
  • Token cookie httpOnly: false and UUID cookie httpOnly: true cannot be overridden (set after cookieOptions spread)

The factory returns {issue, verify}:

  • issue(req, res, acc) — Sets token and UUID cookies on the jar (acc.cookies). No return value.
  • verify(req, res, acc) — Compares the request header token against the cookie using crypto.timingSafeEqual(). Returns undefined on success. On failure, returns {response: {statusCode: 403, detail: 'CSRF verification failed'}}.
Status Condition
403 Forbidden Missing header token, missing UUID cookie, or token verification fails

CSRF attacks exploit a browser behavior: cookies are automatically attached to every request to the cookie’s origin, regardless of which site initiated the request. A malicious page can submit a form or fire a fetch to your API and the browser will include the user’s session cookie — without the user’s knowledge. CSRF tokens prevent this by requiring a secondary proof of intent that the attacking site cannot access.

This means CSRF protection is tied to your authentication mechanism, not your API’s functionality:

Auth mechanism Client type CSRF needed? Rationale
Session cookie Browser Yes Browsers auto-attach cookies — CSRF prevents forged submissions
Bearer token Mobile/service No Tokens are explicitly attached by the client — not auto-sent by the browser
Cookie + Bearer (mixed) Both Per-route Enable CSRF on cookie-auth routes, disable on Bearer-only routes
None (public) Any No No credentials to protect

For APIs that serve both browser and token-based clients, configure CSRF per-route — enable it on cookie-authenticated routes and disable it on Bearer-only routes with csrf: false. See Mixed-Auth CORS & CSRF for the complete implementation pattern.

For configuring authentication strategies (Bearer, Basic, API key), see the authorization middleware guide.

import {compose, cookie, csrf} from '@centralping/ergo';
const csrfMiddleware = csrf({secret: process.env.CSRF_SECRET});
// Issue tokens on GET
const issuePipeline = compose(
cookie(),
{fn: csrfMiddleware.issue, setPath: 'csrf'},
);
// Verify tokens on POST
const verifyPipeline = compose(
cookie(),
{fn: csrfMiddleware.verify, setPath: 'csrf'},
);
  • Mixed-Auth CORS & CSRF — Configuring CORS and CSRF together for browser and token-based clients
  • Secure Mutations — Integrated CSRF + auth + idempotency workflow with client-side fetch wiring

See the auto-generated csrf API docs.