From b5172a0cdbc02686ae905d65ec2392ead41be139 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Fri, 22 Apr 2022 10:41:01 -0300 Subject: [PATCH] Fix csrf cleanup for Rails 7.1 (main) Rails implemented a CSRF token storage strategy to allow storing the CSRF tokens outside of the sessios (for example, in an encrypted cookie), and changed how the value is kept around during the request cycle, by using a request.env value. We still want to ensure the final session value is cleaned correctly in the test, but the implementation needed to change since we can't simply delete from the session anymore, we need to make sure we call the Rails methods for resetting the current storage strategy so it works with all of them. https://github.com/rails/rails/pull/44283 --- lib/devise/hooks/csrf_cleaner.rb | 8 +++++++- test/integration/authenticatable_test.rb | 12 ++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/devise/hooks/csrf_cleaner.rb b/lib/devise/hooks/csrf_cleaner.rb index d725fbc4..211bcab8 100644 --- a/lib/devise/hooks/csrf_cleaner.rb +++ b/lib/devise/hooks/csrf_cleaner.rb @@ -4,6 +4,12 @@ Warden::Manager.after_authentication do |record, warden, options| clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) || warden.winning_strategy.clean_up_csrf? if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy - warden.request.session.try(:delete, :_csrf_token) + request = warden.request + if request.respond_to?(:controller_instance) && request.controller_instance.respond_to?(:reset_csrf_token) + # Rails 7.1+ + request.controller_instance.reset_csrf_token(request) + else + request.session.try(:delete, :_csrf_token) + end end end diff --git a/test/integration/authenticatable_test.rb b/test/integration/authenticatable_test.rb index a1bf28da..6c3be3b5 100644 --- a/test/integration/authenticatable_test.rb +++ b/test/integration/authenticatable_test.rb @@ -346,10 +346,18 @@ class AuthenticationSessionTest < Devise::IntegrationTest test 'refreshes _csrf_token' do swap ApplicationController, allow_forgery_protection: true do get new_user_session_path - token = request.session[:_csrf_token] + token_from_session = request.session[:_csrf_token] + + if Devise::Test.rails71_and_up? + token_from_env = request.env["action_controller.csrf_token"] + end sign_in_as_user - assert_not_equal request.session[:_csrf_token], token + assert_not_equal request.session[:_csrf_token], token_from_session + + if Devise::Test.rails71_and_up? + assert_not_equal request.env["action_controller.csrf_token"], token_from_env + end end end