CSRF Protection
Cross-Site Request Forgery (CSRF) protection prevents malicious websites from submitting forms on behalf of authenticated users. Guren provides built-in CSRF middleware that integrates seamlessly with sessions.
CSRF Protection
Cross-Site Request Forgery (CSRF) protection prevents malicious websites from submitting forms on behalf of authenticated users. Guren provides built-in CSRF middleware that integrates seamlessly with sessions.
Setup
Enable CSRF protection by adding the middleware to your application:
// src/app.ts
import { createApp, createSessionMiddleware, createCsrfMiddleware } from '@guren/core'
const app = createApp()
// Session middleware is required for CSRF
app.use('*', createSessionMiddleware())
app.use('*', createCsrfMiddleware())
The middleware automatically:
- Generates a unique token per session
- Validates tokens on state-changing requests (POST, PUT, PATCH, DELETE)
- Allows safe methods (GET, HEAD, OPTIONS) without validation
Including the Token in Forms
Use the csrfField() helper to generate a hidden input field:
// In your controller
import { Controller, getCsrfToken, csrfField } from '@guren/core'
import { pages } from '@/.guren/pages.gen'
export default class FormController extends Controller {
create() {
const token = getCsrfToken(this.ctx)
// Pass to your template/view
return this.inertia(pages.forms.Create, { csrfToken: token })
}
}
In your frontend form (React example):
function CreateForm({ csrfToken }: { csrfToken: string }) {
return (
<form method="POST" action="/posts">
<input type="hidden" name="_csrf" value={csrfToken} />
{/* form fields */}
<button type="submit">Create</button>
</form>
)
}
Or generate the hidden field directly:
const hiddenField = csrfField(ctx)
// Returns: <input type="hidden" name="_csrf" value="..." />
AJAX Requests
For JavaScript/AJAX requests, include the token in a header:
// Get token from meta tag or cookie
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content
fetch('/api/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken,
},
body: JSON.stringify({ title: 'Hello' }),
})
The middleware checks both the _csrf form field and X-CSRF-Token header.
Configuration Options
createCsrfMiddleware({
// Custom form field name (default: '_csrf')
fieldName: '_token',
// Custom header name (default: 'X-CSRF-Token')
headerName: 'X-XSRF-Token',
// Routes to exclude from CSRF validation
exclude: ['/api/webhooks/*', '/api/public/*'],
// Custom error handler
onError: (ctx) => {
return ctx.json({ error: 'Invalid CSRF token' }, 403)
},
})
Excluding Routes
Some routes (like webhook endpoints) should skip CSRF validation:
createCsrfMiddleware({
exclude: [
'/api/webhooks/stripe',
'/api/webhooks/github',
'/api/public/*', // Wildcard patterns supported
],
})
Manual Token Verification
For custom validation logic, use verifyCsrfToken():
import { verifyCsrfToken, getCsrfToken } from '@guren/core'
import { Router } from '@guren/core'
export function registerWebRoutes(router: Router): void {
router.post('/custom', async (ctx) => {
const token = ctx.req.header('X-Custom-Token')
if (!verifyCsrfToken(ctx, token)) {
return ctx.json({ error: 'Invalid token' }, 403)
}
return ctx.json({ ok: true })
})
}
Token Regeneration
Tokens are tied to the session and regenerate when:
- A new session is created
session.regenerate()is called (recommended after login)
// After successful login
const session = getSessionFromContext(ctx)
await session.regenerate()
// New CSRF token is generated automatically
Security Best Practices
- Always use HTTPS - Tokens can be intercepted over HTTP
- Regenerate after login - Prevents session fixation attacks
- Don't expose tokens in URLs - Use POST bodies or headers
- Set secure cookie flags - The session middleware handles this automatically
Inertia.js Integration
When using Inertia.js, CSRF is handled automatically through cookies. Ensure your Axios/fetch configuration includes credentials:
// resources/js/app.tsx
axios.defaults.withCredentials = true
Inertia automatically reads the XSRF-TOKEN cookie and includes it in requests.