CSRF Plugin
Server-side protection against Cross-Site Request Forgery attacks using the Double-Submit Cookie pattern
Overview
The CSRF plugin provides server-side protection against Cross-Site Request Forgery (CSRF) attacks using the Double-Submit Cookie pattern integrated into Authula's lifecycle hook system. It automatically generates cryptographically secure tokens for safe HTTP methods and validates them on state-changing requests.
Features
- Double-Submit Cookie Pattern — Validates CSRF tokens in both cookies and request headers
- Automatic Token Generation — Generates secure tokens on safe HTTP methods (GET, HEAD, OPTIONS)
- Selective Enforcement — Only validates on routes that explicitly request protection
- Token Cleanup — Automatically clears CSRF cookies on sign-out
- Optional Header Protection — Supports Go 1.25's CrossOriginProtection for additional security
Configuration
Standalone Mode
[plugins.csrf]
enabled = true
cookie_name = "authula_csrf_token"
header_name = "X-AUTHULA-CSRF-TOKEN"
max_age = "24h"
secure = true
same_site = "lax"
enable_header_protection = falseLibrary Mode
import (
csrfplugin "github.com/Authula/authula/plugins/csrf"
)
csrfplugin.New(csrfplugin.CSRFPluginConfig{
Enabled: true,
CookieName: "authula_csrf_token",
HeaderName: "X-AUTHULA-CSRF-TOKEN",
MaxAge: 24 * time.Hour,
Secure: false,
SameSite: "lax",
EnableHeaderProtection: false,
}),Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable or disable the plugin |
cookie_name | string | "authula_csrf_token" | Name of the CSRF cookie |
header_name | string | "X-AUTHULA-CSRF-TOKEN" | Name of the CSRF header |
max_age | duration | "24h" | Token lifetime before expiration |
secure | boolean | false | Force Secure flag on cookies |
same_site | string | "lax" | SameSite attribute: lax, strict, or none |
enable_header_protection | boolean | false | Enable Go's CrossOriginProtection |
API Reference
The CSRF plugin doesn't expose any HTTP endpoints.
Database Schema
The CSRF plugin doesn't create any database tables.
Plugin Capabilities
The CSRF plugin integrates with Authula through the lifecycle hook system, registering hooks at specific stages:
- Before Hook (Order 5) — Token generation for safe methods (GET, HEAD, OPTIONS)
- Before Hook (Order 5) — Token validation for unsafe methods (POST, PUT, PATCH, DELETE) — requires explicit opt-in via route metadata
- After Hook (Order 5) — Cookie cleanup on sign-out
It also has the following capabilities:
csrf.protect— Validates the presence and correctness of CSRF tokens on protected routes
Token Generation Flow
On safe HTTP methods, the plugin automatically generates a cryptographically secure token and sets it as a cookie. The cookie is intentionally readable by JavaScript (HttpOnly=false) to enable the Double-Submit pattern.
Token Validation Flow
On state-changing methods, the plugin validates that the CSRF token exists in both:
- A cookie (readable by JavaScript)
- A request header
Both values must match exactly. Requests with missing or mismatched tokens are rejected with a 403 Forbidden response.
Optional Layered Protection
Beyond the Double-Submit pattern, the plugin optionally supports header-based origin validation using Go 1.25's CrossOriginProtection. When enabled via enable_header_protection, this validates Sec-Fetch-Site, Origin, and Host headers against configured trusted origins before token validation occurs.
How to Use
Routes must explicitly request CSRF protection by including the plugin identifier in their metadata. This allows selective protection—authentication endpoints like sign-in can skip validation while authenticated endpoints enforce it.
[[route_mappings]]
method = "POST"
path = "/some/path"
plugins = ["csrf.protect"]Client Plugin
If you're using the Authula SDK, add the plugin to the SDK like so:
import { createClient } from "authula";
import { CSRFPlugin } from "authula/plugins";
export const authulaClient = createClient({
url: "http://localhost:8080/auth",
plugins: [
// other plugins...
new CSRFPlugin({
cookieName: "authula_csrf_token", // optional, defaults to "authula_csrf_token"
headerName: "X-AUTHULA-CSRF-TOKEN", // optional, defaults to "X-AUTHULA-CSRF-TOKEN"
}),
],
});Key Design Decisions
- HttpOnly=false — Required for Double-Submit pattern where JavaScript must read the cookie value
- Selective enforcement — Only validates on routes that explicitly request protection
- Defense in depth — Optional header protection provides additional layer beyond tokens
- Hot reloadable — Configuration changes apply without server restart
Security Recommendations
- Enable Secure Flag in Production — Set
secure = trueto ensure cookies are only transmitted over HTTPS - Strict SameSite — Set
same_site = "strict"for maximum CSRF protection if you need it (may impact UX). But you may want to keep it atlaxto still allow some cross-site requests especially for top-level navigations. - Enable Header Protection — Consider enabling
enable_header_protection = truefor additional origin validation - Trusted Origins — When using header protection, configure trusted origins appropriately for your deployment
- Token Rotation — The 24-hour default
max_ageprovides a balance between security and UX; adjust based on your security requirements - HttpOnly Trade-off — The Double-Submit pattern requires
HttpOnly=false, making tokens accessible to JavaScript; ensure your application is protected against XSS attacks
