Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up Rails CSRF to play nice with Axios default CSRF behavior #96

Merged
merged 2 commits into from
Aug 21, 2023

Conversation

bknoles
Copy link
Collaborator

@bknoles bknoles commented Jun 26, 2023

Resolves #71

More discussion there and #72

@bknoles bknoles requested a review from BrandonShar June 26, 2023 14:45
@@ -11,6 +11,10 @@ module Controller
InertiaRails.share(errors: session[:inertia_errors]) if session[:inertia_errors].present?
end
helper ::InertiaRails::Helper

after_action do
cookies['XSRF-TOKEN'] = form_authenticity_token unless request.inertia? || !protect_against_forgery?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is essentially lifted from @buhrmi 's #72

@@ -89,6 +90,10 @@ def force_refresh(request)
request.flash.keep
Rack::Response.new('', 409, {'X-Inertia-Location' => request.original_url}).finish
end

def copy_xsrf_to_csrf!
@env['HTTP_X_CSRF_TOKEN'] = @env['HTTP_X_XSRF_TOKEN'] if @env['HTTP_X_XSRF_TOKEN'] && inertia_request?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes basically translate Axios's CSRF defaults into Rails's CSRF defaults, and only for Inertia requests

@@ -96,4 +96,40 @@

it { is_expected.to eq 302 }
end

describe 'CSRF' do
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I set out to actually test the entire CSRF flow. I wanted:

  1. A test showing that an X-CSRF-Token header passes CSRF (essentially verifying Rails default behavior)
  2. A failing test showing that an X-XSRF-Token (like Axios will send) fails CSRF
  3. Write the code that translates Axios to Rails
  4. See that red test turn a sweet green

Turns out that's a nontrivial thing to do! RSpec request tests don't give you access to a session before a request, so it's really difficult to do something like mocking out a form_authenticity_token.

System tests are better suited for testing stateful session based workflows, but the amount of setup required for that did not feel worth it here.

I learned a lot about Rails's CSRF internals along the way!

@@ -0,0 +1,11 @@
module HelperModule
def with_forgery_protection
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted this for my theoretical full CSRF testing as described above. Still somewhat useful to verify that we only add the XSRF-Token cookie on requests with forgery protection.

@PedroAugustoRamalhoDuarte
Copy link
Contributor

It's an excellent feature that enables quick setup of Inertia client-side following the website https://inertiajs.com/client-side-setup

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Making CSRF protection work out of the box
2 participants