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_token and 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_token and 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_tocommit_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 includeActionController::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.
Environment
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 fromActionController::RequestForgeryProtection
which is mixed intoActionController::Base
and so is automatically available to most controllers.Devise::FailureApp
however extendsActionController::Metal
(notActionController::Base
) so it does not include this method.Current behavior
Using Rails 7.1 (or higher) and
redis_store
for session storage:form_authenticity_token
and 2) calls:authenticate_user!
Expected behavior
Using Rails 7.1 (or higher) and
redis_store
for session storage:form_authenticity_token
and 2) calls:authenticate_user!
The problem is that at this line the
controller_instance
is aDevise::FailureApp
which does notrespond_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
inDevise::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.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.