Class: Sinatra::Inertia::CSRFMiddleware
- Inherits:
-
Object
- Object
- Sinatra::Inertia::CSRFMiddleware
- Defined in:
- lib/sinatra/inertia/csrf_middleware.rb
Overview
Rack middleware implementing the Inertia / Laravel “double-submit cookie” CSRF pattern that ‘@inertiajs/react` and `@inertiajs/vue3` honour out of the box.
Behaviour
-
On every request, ensure a token cookie named ‘XSRF-TOKEN` is present (generate + set on the response if missing).
-
For non-safe methods (POST / PUT / PATCH / DELETE), require the request to send ‘X-XSRF-TOKEN` whose value matches the cookie. Mismatch → `403 Forbidden`.
-
The cookie is not HttpOnly — Inertia’s client reads it from ‘document.cookie` and forwards it as `X-XSRF-TOKEN` automatically.
Caveats
Double-submit cookie is the standard Inertia/Laravel pattern but is weaker than synchronizer-token CSRF when an attacker has any script-injection foothold. Pair with:
* `SameSite=Lax` (default below) — the cookie won't ride
cross-site form posts.
* Strict CSP / no XSS.
* Optionally, server-side session-bound tokens via
`Rack::Protection::AuthenticityToken` instead.
Configuration
‘set :inertia_csrf_protection, false` disables this middleware. Use this only when the consumer ships its own CSRF defence. Safer defaults assume the gem is responsible.
Constant Summary collapse
- COOKIE_NAME =
'XSRF-TOKEN'- HEADER_KEY =
'HTTP_X_XSRF_TOKEN'- ENV_TOKEN_KEY =
'sinatra.inertia.csrf_token'- SAFE_METHODS =
%w[GET HEAD OPTIONS].freeze
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, same_site: :Lax) ⇒ CSRFMiddleware
constructor
A new instance of CSRFMiddleware.
Constructor Details
#initialize(app, same_site: :Lax) ⇒ CSRFMiddleware
Returns a new instance of CSRFMiddleware.
44 45 46 47 |
# File 'lib/sinatra/inertia/csrf_middleware.rb', line 44 def initialize(app, same_site: :Lax) @app = app @same_site = same_site end |
Instance Method Details
#call(env) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/sinatra/inertia/csrf_middleware.rb', line 49 def call(env) existing = (env) token = existing || SecureRandom.urlsafe_base64(32) env[ENV_TOKEN_KEY] = token unless safe_method?(env) header = env[HEADER_KEY].to_s if existing.nil? || header.empty? || !secure_compare(header, existing) return forbidden('CSRF token mismatch (expected matching X-XSRF-TOKEN header to XSRF-TOKEN cookie)') end end status, headers, body = @app.call(env) unless existing == token (headers, token) end [status, headers, body] end |