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

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 mechanismClient typeCSRF needed?Rationale
Session cookieBrowserYesBrowsers auto-attach cookies — CSRF prevents forged submissions
Bearer tokenMobile/serviceNoTokens are explicitly attached by the client — not auto-sent by the browser
Cookie + Bearer (mixed)BothPer-routeEnable CSRF on cookie-auth routes, disable on Bearer-only routes
None (public)AnyNoNo 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 { csrf } from "@centralping/ergo";
OptionTypeDefaultDescription
secretstringRequired. HMAC secret for token signing
cookieTokenNamestring'CSRF-TOKEN'Cookie name for the CSRF token
headerTokenNamestring'X-CSRF-TOKEN'Request header name for the token
cookieUuidNamestring'CSRF-UUID'Cookie name for the CSRF UUID
encodingstring'base64'Token encoding
cookieOptionsobject{}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 (default from cookie module), sameSite: 'Strict' (locked)
  • sameSite cannot be overridden via cookieOptions on either cookie
  • Token cookie httpOnly: false 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'}}.
StatusCondition
403 ForbiddenMissing header token, missing UUID cookie, or token verification fails
import { compose, cookie, csrf } from "@centralping/ergo";
const csrfMiddleware = csrf({ secret: process.env.CSRF_SECRET });
// Issue tokens on GET
const issuePipeline = compose(
{fn: cookie(), setPath: "cookies"},
{fn: csrfMiddleware.issue, setPath: "csrf"},
);
// Verify tokens on POST
const verifyPipeline = compose(
{fn: cookie(), setPath: "cookies"},
{fn: csrfMiddleware.verify, setPath: "csrf"},
);

See the auto-generated csrf API docs.