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

ActionController::InvalidAuthenticityToken in Devise::SessionsController#create #5652

Open
venkat071982 opened this issue Nov 6, 2023 · 14 comments

Comments

@venkat071982
Copy link

venkat071982 commented Nov 6, 2023

Ruby 3.2.2
Rails 7.0.8
Devise 4.9.2

Current behavior

Extremely frustrating. All of a sudden I try to login/register I get Can't verify CSRF token authenticity..

I didn't do any changes and it was all working fine 1h ago.

Stack trace:

....
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken, warning_message
end
end
end
...
PS: Yes I have the CSRF and CSP tags:

<%= csrf_meta_tags %>
<%= csp_meta_tag %>

@venkat071982
Copy link
Author

Screenshot from 2023-11-06 19-57-53

@adnanpirota
Copy link

I'm stuck on this too, but only in production

@DonatienD
Copy link

Encoutering the exact same on my local server, with ActiveAdmin.

@venkat071982
Copy link
Author

venkat071982 commented Nov 8, 2023

I am using redis store
in rails7 syntax changed so that its not working
I did like following in session_store.rb its working fine
config.session_store :redis_store, servers: "redis://localhost:6379/0/session", key: '_your_app_session_key', expires_in: 1.day, prefix: 'myapp:sessions:'

@DonatienD
Copy link

We fixed it on our end by removing the following from config/application.rb:

config.action_dispatch.cookies_same_site_protection = :none
config.action_controller.default_protect_from_forgery = false if ENV['RAILS_ENV'] == 'development'

@wenlingang
Copy link

I had the same problem when upgrading from rails 7.0 to 7.1

@leoplct
Copy link

leoplct commented Dec 1, 2023

Same issue after upgrading to Rails 7.1.2

It works on development, but not on staging/production

FORM (SIGN UP)
Head

Form
<form data-turbo="false" class="space-y-4" id="new_user" action="/en/users" accept-charset="UTF-8" method="post">

Registration controller

class Users::RegistrationsController < Devise::RegistrationsController
   before_action :configure_sign_up_params, only: [:create]

def configure_sign_up_params
     devise_parameter_sanitizer.permit(:sign_up,
       keys: [:email, :password, :password_confirmation, :terms_of_service, :coupon_code, :currency_code, :referral, :locale, :referrer, :via, utm: [:source, :campaign, :medium, :content, :term] ])
   end

config/enviroment/staging.rb

  config.cache_store = :redis_cache_store, { url: ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" }}
  config.session_store :cookie_store, key: "_session", expire_after: 1.year

Gemfile.lock
devise (4.9.3)
rails (7.1.2)

I noticed in dev console that the session_id is named ["session_id

@alexdesi
Copy link

I had the same problem when upgrading from rails 7.0 to 7.1

Same for me, can I ask you how did you fix?

@leoplct
Copy link

leoplct commented Dec 11, 2023

I had the same problem when upgrading from rails 7.0 to 7.1

Same for me, can I ask you how did you fix?

Upgrade to ruby 3.2, then be sure to have

config/enviroment/production.rb

config.session_store :cookie_store, key: "_example_session", expire_after: 1.year, domain: ".example.com"

app/controller/application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery  with: :exception

If you use Passenger, then add this to Gemfile. I was inspecting the Chrome Dev console and the cookie name cointaned a unrecognized character like ["session_id instead of session_id.

gem 'rack', '~> 2.2'

@wenlingang
Copy link

I had the same problem when upgrading from rails 7.0 to 7.1

Same for me, can I ask you how did you fix?

I found that changing the session and so on didn't work, and then upgrading to rails 7.1.2 solved it

@grin
Copy link

grin commented Jan 10, 2024

I was running into the same issue with Rails 7.1.2 and Devise 4.9.3, what fixed it in my case (based on this comment: #5298 (comment)) was the puma upgrade to 6.4.2 from 5.6.4.

@GuillaumeGillet
Copy link

Same problem with rails 6.1

@serg-kovalev
Copy link

serg-kovalev commented Jan 18, 2024

In my case I fixed it in config/initializers/session_store.rb:
by adding domain: :all and changing cookie_store key to handle various environments like _#{Rails.env}.
Something like that:

Rails.application.config.session_store :cookie_store, key: "_my_name_#{Rails.env}", expire_after: 1.year, domain: :all

Also I'm using nginx in development, so, for my proxy I've ensured that the host header is correct to handle same origin verification problem (if you have a frontend on a separate localhost:port) and combining them together on the same host by using nginx:

location / {
      proxy_set_header HOST $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_pass_request_headers on;
      proxy_pass   http://rails_backend;
      proxy_buffers 8 1024k;  
      proxy_buffer_size 1024k;
  }

Note: rails_backend is the upstream. I had issues when I used $host, but not $http_host

Might be useful for someone like myself who struggled with the issue for a few hours

@jean-francois-labbe
Copy link

Faced similar issue, here is the story. Maybe it could help someone.

When removing domain: :all, tld_length: 2 from the session_store configuration.
Going from:

# kept only meaningful elements of the configuration
Rails.application.config.session_store :redis_store,
  key: "_session_identifier", expire_after: 14.days, domain: :all, tld_length: 2, secure: true

To

Rails.application.config.session_store :redis_store,
  key: "_session_identifier", expire_after: 14.days, secure: true

The issue was visible immediately after deployment, no need to way for stale cookies...

Context
Devise is configured to use the :rememberable feature (remember_user_token cookie)
We moved an application from the domain (example.com) to multiple subdomains (eu.example.com and now us.example.com) with different application servers. During the migration we wanted users to stay logged in, that's why domain: :all, tld_length: 2 to the session_store configuration, so that cookies on example.com could be reused on eu.example.com domain.

Before the change the server would set a _session_identifier cookie on the .example.com domain.
After removing domain: :all, tld_length: 2. This resulted in the server generating a new _session_identifier cookie with the same name as the previous one but set on eu.example.com.

As the user was identified by the remember_user_token, users would still be logged in. But when submitting a form they would face the ActionController::InvalidAuthenticityToken.

Why ?
After deployment. When getting a page the server generates a csrf_token which is stored in the session and inserted in the html page (meta tag and hidden form field). the session uses the new configuration no domain: :all, tld_length: 2. which results in a new _session_identifier cookie sent to the browser.

When submitting the form the browser would send the two _session_identifier cookies, one generated when the configuration was domain: :all, tld_length: 2 and one new generated with domain: :all, tld_length: 2.
According to the HTTP RFC:

  • Among cookies that have equal-length path fields, cookies with earlier creation-times are listed before cookies with later creation-times.
    This results in the browser sending the oldest _session_identifier cookie first (the one created with domain: :all, tld_length: 2) and the latest _session_identifier cookie last. cookies have different values (session identifier).

The server will use the oldest _session_identifier cookie to retreive a session from the session store. The old session would not contain the same csrf_token as the one submitted by the browser.

How did I fix it ?

  1. Changed the session cookie name
#   Users would still be logged in thanks to the `remember_user_token`

Rails.application.config.session_store :redis_store,
  key: "_new_session_identifier", expire_after: 14.days, secure: true
  1. Cleanup previous cookies
class ApplicationController < ActionController::Base
  before_action do
    if request_format == :html
      cookies.delete(: _session_identifier, domain: :all)
      cookies.delete(:remember_user_token, domain: :all)
      current_user.remember_me! if current_user
    end
  end
end

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

No branches or pull requests

10 participants