CSRF
ingenium.csrf protects state-changing requests against cross-site request forgery. It supports two storage modes - cookie-based double-submit (default, no session needed) and synchronizer-pattern (when you're already running sessions).
Usage
import { ingenium } from 'ingenium'
const app = ingenium()
app.use(ingenium.csrf({
secret: process.env.CSRF_SECRET!, // required for cookie storage
storage: 'cookie', // 'cookie' (default) | 'session'
cookie: { sameSite: 'lax', secure: true },
ignoreMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE'],
// skip: (ctx) => ctx.path.startsWith('/api/webhooks/'), // opt-out
}))
app.get('/form', (ctx) => {
// ctx.csrfToken() returns the current token to embed in HTML / send to a JS client.
return `<form method="POST" action="/submit">
<input type="hidden" name="_csrf" value="${ctx.csrfToken()}">
<button>Submit</button>
</form>`
})
app.post('/submit', async (ctx) => {
// CSRF middleware already validated; if we got here the token is good.
const body = await ctx.body.json()
return { ok: true, body }
})
Storage modes
cookie(default, no session needed) - double-submit cookie pattern with HMAC-signed tokens. The token is written to a non-HttpOnlycookie on safe requests; the client must echo it back viaX-CSRF-Token(orX-XSRF-Tokenfor Angular, or?_csrf=query param) on unsafe requests. Same-origin policy plus HMAC verification together prevent forgery.session- synchronizer pattern. Token stored onctx.session.csrfToken; submitted token compared against it. RequiressessionMiddlewareto run first; throws a clear developer error if missing.
Verification
Verification uses crypto.timingSafeEqual. Secret rotation supported (secret: ['new', 'old']). Failures throw IngeniumCsrfError (HTTP 403, code CSRF_FAILED) which the default error boundary serializes; catch in app.onError for custom handling.
Sessioned apps should opt in to
storage: 'session'. The default is'cookie'because it's self-contained, but if you're already runningsessionMiddlewarethe synchronizer pattern is simpler (one cookie instead of two, and rotating the session secret rotates CSRF protection automatically). We don't auto-detect because middleware order shouldn't change semantics.
Where to next?
- Sessions - required for
storage: 'session'. - CORS - companion protection for browser requests.
- Errors -
IngeniumCsrfErrorhandling. - Production hardening - the full prod wiring.