csrf-sync

Stateful CSRF protection for Express using the Synchronizer Token Pattern, built to replace the deprecated csurf package.

Library
npm
v4.2.1
41stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
21/100Needs Attention
Development Activity0
Maintenance0
Community20
Maturity52
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture78
Code Quality85
Innovation75
Learning Curve70

csrf-sync is a lightweight Express middleware that implements the OWASP-recommended Synchronizer Token Pattern for CSRF protection. It was built as a direct, actively maintained replacement for the deprecated csurf package, matching its default error shape and status codes to make migration straightforward while fixing the configurability gaps that made many CSRF setups accidentally insecure.

Every piece of the token lifecycle - where the token is read from on incoming requests, where it’s stored server-side, and how a new one is generated or revoked - is exposed as an injectable function with sensible defaults, so teams can plug in session storage, a custom header, or a hidden form field without forking the library. The package ships as dual ESM/CJS with full TypeScript types, and augments Express’s Request and SessionData types directly so req.csrfToken() works out of the box.

What You Get

  • A drop-in csrfSynchronisedProtection Express middleware that validates the Synchronizer Token Pattern on every state-changing request
  • Injectable token lifecycle functions - generateToken, revokeToken, getTokenFromRequest, getTokenFromState, storeTokenInState - for full control over storage and transmission
  • Type-safe req.csrfToken() and req.session.csrfToken via built-in Express/express-session type augmentation
  • A skipCsrfProtection escape hatch for selectively bypassing protection (e.g. for trusted native-app clients) without wrapping the middleware yourself
  • Two runnable example apps (a minimal Express server and a full React + Vite + Express stack) plus a dedicated FAQ covering header/body/form token transmission

Common Use Cases

  • Migrating an Express app off the deprecated csurf package with minimal config changes
  • Protecting server-rendered form submissions with a hidden CSRF token input
  • Protecting JSON API routes consumed by a same-origin SPA, using an x-csrf-token header fetched from a dedicated token endpoint
  • Selectively exempting specific request sources (e.g. verified native mobile clients) from CSRF checks via skipCsrfProtection

Under The Hood

Architecture csrf-sync is a single-module factory: calling csrfSync() (in src/index.ts) returns a fully wired protection object built from small, independently swappable pure functions - getTokenFromRequest, getTokenFromState, storeTokenInState, generateToken, revokeToken, isRequestValid, and the csrfSynchronisedProtection Express middleware itself. Every one of these is injectable via the options object, so token storage (session, Redis, custom store) and transmission (header, body, cookie-adjacent patterns) can be swapped without touching the library’s internals - the middleware only orchestrates requiresCsrfProtection then isRequestValid then next()/next(error). Module augmentation (declare module “express-session” / “express-serve-static-core”) extends Request and SessionData types directly, so req.csrfToken() and req.session.csrfToken are type-safe without any wrapper types leaking into consumer code.

Tech Stack Written in TypeScript, built with tsup into dual ESM/CJS bundles with separate .d.ts/.d.cts type declarations, targeting Express + express-session as peer runtime dependencies and http-errors for constructing the thrown CSRF error. Linting/formatting is handled by Biome (biome.json, enforced in CI as a separate job), tests run under Vitest with @vitest/coverage-istanbul for coverage reporting, and releases are versioned via commit-and-tag-version.

Code Quality Tests live under src/tests/ and use a reusable “test suite” function (src/tests/suite/csrfsync.ts) parameterized by configuration and an “overwrite mock token” callback, then instantiated twice in csrfsync.test.ts - once for the default header-based config, once for a body-based token with a custom error config - plus a dedicated skipCsrfProtection.test.ts for the opt-out escape hatch. This DRY pattern means every configuration variant exercises the same assertions without duplicated test bodies. CI runs three separate jobs (lint via Biome, build via tsup, and test-with-coverage uploaded to Coveralls), so quality gates are enforced per-PR rather than left to convention.

API Design The public surface is a single factory call, csrfSync(options), with every option optional and defaulting to values chosen to mirror the deprecated csurf package (same default status code, message, and EBADCSRFTOKEN code) to ease migration. Getting started is three lines: install, import, destructure csrfSynchronisedProtection and mount it after the session middleware. The README and a dedicated FAQ.md walk through header-based, body-based, and form-based token transmission, and the repo ships two runnable examples (a minimal Express app and a full React + Vite + Express example) rather than just code snippets.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search