Skip to content

Commit dc25c69

Browse files
committed
Make Devise::FailureApp commit the CSRF token on Rails 7.1+.
1 parent 9ea459d commit dc25c69

3 files changed

Lines changed: 30 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### Unreleased
2+
3+
* bug fixes
4+
* Fix `Devise::FailureApp` not committing the CSRF token to the session on Rails 7.1+, which dropped the session and CSRF token on authentication failure. [#5851](https://github.com/heartcombo/devise/pull/5851)
5+
16
### 5.0.4 - 2026-05-08
27

38
* security fixes

lib/devise/failure_app.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ class FailureApp < ActionController::Metal
1111
include ActionController::UrlFor
1212
include ActionController::Redirecting
1313

14+
# Rails 7.1+ defers writing the CSRF token to the session and commits it at
15+
# the end of the request through the controller instance. FailureApp becomes
16+
# that instance, so it needs +commit_csrf_token+ from RequestForgeryProtection
17+
# to avoid silently dropping the token (and the session) on auth failure.
18+
include ActionController::RequestForgeryProtection
19+
1420
include Rails.application.routes.url_helpers
1521
include Rails.application.routes.mounted_helpers
1622

test/failure_app_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,4 +479,23 @@ def call_failure(env_params = {})
479479
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
480480
end
481481
end
482+
483+
# Rails 7.1+ defers writing the CSRF token to the session and commits it at the
484+
# end of the request through the controller instance (see rails/rails#44283).
485+
# Devise::FailureApp becomes that controller instance, so it must respond to
486+
# +commit_csrf_token+ or the deferred token is silently dropped and no session
487+
# is persisted on authentication failure.
488+
if ActionDispatch::Request.method_defined?(:commit_csrf_token)
489+
context "Committing the CSRF token" do
490+
test "stores the deferred CSRF token in the session" do
491+
csrf_token = "a-csrf-token"
492+
request = ActionDispatch::Request.new(
493+
"rack.session" => {},
494+
ActionController::RequestForgeryProtection::CSRF_TOKEN => csrf_token
495+
)
496+
Devise::FailureApp.new.commit_csrf_token(request)
497+
assert_equal csrf_token, request.session[:_csrf_token]
498+
end
499+
end
500+
end
482501
end

0 commit comments

Comments
 (0)