The Basics
CSRF protection
Introduction
Cross-site request forgery (CSRF) tricks a logged-in user’s browser into submitting a state-changing request the user did not intend. JCC Express MVC mitigates this with a session-bound token: each session gets a random value; unsafe requests must repeat that value in the body or in a header so only your pages and scripts can succeed.
The csrf() middleware (from jcc-express-mvc/Core) stores the token in the session, exposes it to templates and code, and verifies it on POST, PUT, PATCH, and DELETE when verification applies.
It must run after express-session (and cookie parsing). The default application stack registers session before your HTTP kernel’s middlewares array, so placing csrf() in app/Http/kernel.ts is correct. Session usage is covered in Session.md.
Enabling CSRF
Add the factory to your kernel’s global middleware:
If req.session is missing, csrf() logs a warning and skips verification so the app does not hard-fail; in production you should ensure session middleware is present.
When verification runs
- Safe methods (GET, HEAD, OPTIONS, …) — no check; the token is still created or refreshed in session and exposed for the response.
- Unsafe methods (POST, PUT, PATCH, DELETE) — the submitted token must match the session token, unless a skip rule applies (below).
Skip rules
API prefix
By default skipApi is true: any path whose req.path starts with /api skips CSRF. That matches a common split where route/api.ts is mounted under /api in bootstrap/app.ts.
State-changing JSON APIs under another prefix should either use except, set skipApi: false and send the token (or use other API auth such as tokens), or mount those routes under /api.
Exclusions
Pass except to skip verification for specific paths (exact match or prefix with a trailing slash rule, or RegExp):
String rules: path equals the string, or path starts with string + "/" (see CsrfMiddleware implementation).
CsrfOptions
except— Default[]. Paths (string orRegExp) that skip verification on unsafe methods.sessionKey— Default"_csrf_token". Session key used to store the token.fieldName— Default"_token". Form field name read fromreq.body.headerName— Default"x-csrf-token". Header checked;req.headersis also read with a lowercased key.tokenLength— Default32. Random byte length before hex encoding (the hex string length is twice this).skipApi— Defaulttrue. Whenreq.pathstarts with/api, verification is skipped.
Exposing the token
After csrf() runs for a request:
res.locals._token— use in Blade-style templates (hidden inputs, meta tags).req.csrfToken()— function returning the current token (same value asres.locals._token).
HTML forms (Blade / server templates)
Use the @csrf directive so the form includes a hidden _token field:
For PUT / PATCH / DELETE from a form, combine with @method and method spoofing middleware (see methodSpoofing from jcc-express-mvc/Core):
The directive outputs a hidden input named _token by default, matching fieldName.
JavaScript and SPAs (Inertia, fetch, Axios)
Read the token from the page (for example a meta tag rendered with {{ _token }} or your framework’s equivalent) and send it on unsafe requests:
Header name defaults to x-csrf-token; keep it aligned with headerName if you customize the middleware.
For Inertia, the same idea applies: ensure the shared view or root template exposes the token and that client-side navigations or router.post calls include the header or body field your middleware expects.
Failure behavior
A missing or mismatched token throws MissMatchTokenException (mapped by the framework error handler to an appropriate HTTP response). Customize 419-style handling in your exception layer if needed.
Relationship to route middleware
csrf() is normally global (kernel middlewares), not per-route. Use except / skipApi to narrow scope. Route-level auth or guest is separate; CSRF protects session-cookie-authenticated browser flows regardless of auth middleware order, as long as csrf() runs on those requests.