Devise::FailureApp is not compatible with Rails 7.1 csrf_token_storage
Environment
- Ruby 3.2.2
- Rails 7.1+
- Devise 4.9.4
Overview
Rails 7.1 changed how csrf token storage works and appears to break in Devise under some situations.
For proper behavior Rails now seems to expect controllers to include the commit_csrf_token method from ActionController::RequestForgeryProtection which is mixed into ActionController::Base and so is automatically available to most controllers. Devise::FailureApp however extends ActionController::Metal (not ActionController::Base) so it does not include this method.
Current behavior
Using Rails 7.1 (or higher) and redis_store for session storage:
- Implement a controller that 1) generates a CSRF token by calling for example
form_authenticity_tokenand 2) calls:authenticate_user! - Make an unauthenticated request to that controller
- Observe that no session cookie is returned, no session is created, and the CSRF token is not stored
Expected behavior
Using Rails 7.1 (or higher) and redis_store for session storage:
- Implement a controller that 1) generates a CSRF token by calling for example
form_authenticity_tokenand 2) calls:authenticate_user! - Make an unauthenticated request to that controller
- A session cookie should be returned and the CSRF token should be stored in the session
The problem is that at this line the controller_instance is a Devise::FailureApp which does not respond_to commit_csrf_token. Because of this the CSRF token is not written to the session, the session is not created / initialized, and no session cookie is returned.
Workaround
My current workaround is to include ActionController::RequestForgeryProtection in Devise::FailureApp which appears to fix it but I'm not sure if this could cause any problems or if there are additional cases that would need to be addressed.
module Devise
class FailureApp
include ActionController::RequestForgeryProtection
end
end
I'm really not sure if this should be considered a Rails issue or a Devise issue but I filed a ticket with Rails to make them aware too.
Might the Responder also need it?
https://github.com/heartcombo/devise/blob/a259ff3c28912a27329727f4a3c2623d3f5cb6f2/lib/devise/controllers/responder.rb#L7