Skip to content

Commit 6619f7d

Browse files
committed
Set up Rails CSRF to play nice with Axios default CSRF behavior
1 parent bd658da commit 6619f7d

File tree

5 files changed

+62
-0
lines changed

5 files changed

+62
-0
lines changed

lib/inertia_rails/controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ module Controller
1111
InertiaRails.share(errors: session[:inertia_errors]) if session[:inertia_errors].present?
1212
end
1313
helper ::InertiaRails::Helper
14+
15+
after_action do
16+
cookies['XSRF-TOKEN'] = form_authenticity_token unless request.inertia? || !protect_against_forgery?
17+
end
1418
end
1519

1620
module ClassMethods

lib/inertia_rails/middleware.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def initialize(app, env)
1818
end
1919

2020
def response
21+
copy_xsrf_to_csrf!
2122
status, headers, body = @app.call(@env)
2223
request = ActionDispatch::Request.new(@env)
2324

@@ -89,6 +90,10 @@ def force_refresh(request)
8990
request.flash.keep
9091
Rack::Response.new('', 409, {'X-Inertia-Location' => request.original_url}).finish
9192
end
93+
94+
def copy_xsrf_to_csrf!
95+
@env['HTTP_X_CSRF_TOKEN'] = @env['HTTP_X_XSRF_TOKEN'] if @env['HTTP_X_XSRF_TOKEN'] && inertia_request?
96+
end
9297
end
9398
end
9499
end

spec/inertia/request_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,40 @@
9696

9797
it { is_expected.to eq 302 }
9898
end
99+
100+
describe 'CSRF' do
101+
describe 'it sets the XSRF-TOKEN in the cookies' do
102+
subject { response.cookies }
103+
before do
104+
with_forgery_protection do
105+
get inertia_request_test_path, headers: headers
106+
end
107+
end
108+
109+
context 'it is not an inertia call' do
110+
let(:headers) { Hash.new }
111+
it { is_expected.to include('XSRF-TOKEN') }
112+
end
113+
114+
context 'it is an inertia call' do
115+
let(:headers){ { 'X-Inertia' => true } }
116+
it { is_expected.not_to include('XSRF-TOKEN') }
117+
end
118+
end
119+
120+
describe 'copying an X-XSRF-Token header (like Axios sends by default) into the X-CSRF-Token header (that Rails looks for by default)' do
121+
subject { request.headers['X-CSRF-Token'] }
122+
before { get inertia_request_test_path, headers: headers }
123+
124+
context 'it is an inertia call' do
125+
let(:headers) {{ 'X-Inertia' => true, 'X-XSRF-Token' => 'foo' }}
126+
it { is_expected.to eq 'foo' }
127+
end
128+
129+
context 'it is not an inertia call' do
130+
let(:headers) { { 'X-XSRF-Token' => 'foo' } }
131+
it { is_expected.to be_nil }
132+
end
133+
end
134+
end
99135
end

spec/rails_helper.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
# Prevent database truncation if the environment is production
1010
abort("The Rails environment is running in production mode!") if Rails.env.production?
1111
require 'rspec/rails'
12+
# Require the spec/support directory and its subdirectories.
13+
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
14+
15+
require_relative './support/helper_module'
1216
# Add additional requires below this line. Rails is not loaded until this point!
1317
# Requires supporting ruby files with custom matchers and macros, etc, in
1418
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
@@ -45,6 +49,8 @@
4549
config.filter_rails_from_backtrace!
4650
# arbitrary gems may also be filtered via:
4751
# config.filter_gems_from_backtrace("gem name")
52+
53+
config.include HelperModule
4854
end
4955

5056
require 'rails-controller-testing'

spec/support/helper_module.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module HelperModule
2+
def with_forgery_protection
3+
orig = ActionController::Base.allow_forgery_protection
4+
begin
5+
ActionController::Base.allow_forgery_protection = true
6+
yield if block_given?
7+
ensure
8+
ActionController::Base.allow_forgery_protection = orig
9+
end
10+
end
11+
end

0 commit comments

Comments
 (0)