rails: Rails 5.0.0beta3: form authenticity_token does not work when page loaded by Turbolinks

I have a simple Rails 5.0.0beta3 app with a simple form to create a record. Default configuration and environment in development.

<%= form_for @node, url: admin_book_nodes_url, as: :node do |form| %> <%= render “form”, f: form %>

<%= form.submit %> or <%= link_to "Cancel", admin_book_nodes_path %>

<% end %>

When the form is loaded through a link on another page of the app, on submission a ActionController::InvalidAuthenticityToken is generated.

‘Started POST “/admin/book/nodes” for ::1 at 2016-03-20 11:54:31 +0000 Processing by Admin::Book::NodesController#create as HTML Parameters: {“utf8”=>“✓”, “authenticity_token”=>“/G5pF6hSPx0Vf21Fi0FCh+VlOcHY4w8C5lmHmwr3NQRjfXUP9/xboybeV3tevmyTyHcwSX8LplU/HgZVGDbGlw==”, “node”=>{“parent_id”=>“1”, “position”=>“1”, “title”=>“lkjlkj”, “description”=>“lkjlj”, “published”=>“0”, “content”=>“lkjlkj”}, “commit”=>“Create node”} Can’t verify CSRF token authenticity Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms)’

However if the form page is manually reload before being filled then everything works fine. The behaviour is the same with both Safari and Chrome.

What I have noticed:

  • When the form is loaded from a link on another page (failure case), it is loaded by Turbolinks through an XHR request. In such case the page authenticity token in csrf_meta_tags and the form’s token in authenticity_token have different values.
  • When the form is loaded or reloaded by the browser (successful case), so not by Turbolinks the tokens in in csrf_meta_tags and the form’s authenticity_token are the same.

Disabling Turbolinks specifically for the link to the form (e.g. <a data-turbolinks="false" href="/admin/book/nodes/new">New node</a>) will also prevent the issue.

It does not seem the same as #23524, but perhaps it is related.

Note also my submission on Stackoverflow.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 2
  • Comments: 29 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Using 5.0.1. Form is generate with <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>

Still seeing this issue.

Should this be re-opened? Or are these reports of the issue still happening all false-alerts? (I’m seeing something similar as well (a form loaded with turbolinks throws InvalidAuthenticityToken error). (Rails 5.1.5)

I’ve the same issue:

  • RAILS version: 5.2.0
  • Turbo-links: Disabled
  • Root cause: Form generated with :url parameters and … ?

What that damn hell RAILS is again doing … anyway, the workaround I found on my side (Do not ask me why), is to manually include an other token in the HTML form:

<%= form_for(@xxx, :url => '/xxx', 'data-no-turbolink' => true) do |f| %>  
  <%= error_messages_for @xxx %>                                                             
   ( ... )
   <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token.to_s %>" />
   <%= f.submit 'Submit' %>  
<% end %>

The two tokens will be send when submitting the form, the last one value will override the one crafted by the form helper and … it’s seem to works.

This happened to me even in a Rails 6 app. If someone is looking for a solution, this is what worked for me: $(document).on('turbolinks:load', function(){ $.rails.refreshCSRFTokens(); });

You need rails-ujs or jquery-ujs included with your app. If you’re on Rails 6 I believe you already have rails-ujs by default.

After further tinkering when trying to create a simple app reproducing the issue, it seems that the issue is related to the option url in form_for (which I use in my app, see my initial submission).

To reproduce (rails 5.0.0beta3, ruby 2.3.0p0):

rails new .
rails g model user name:string
rails db:migrate
rails g controller users index new create
bundle exec rails s
class UsersController < ApplicationController
  def index
  end

  def new
    @user = User.new
  end

  def create
  end
end
<h1>Users#index</h1>
<%= link_to "New user", new_user_path %>
<h1>Users#new</h1>
<%= form_for @user do |f| %>
<p><%= f.text_field :name %></p>
<p><%= f.submit %></p>
<% end %>
Rails.application.routes.draw do
  resources :users
end

The form must be accessed by following the link in the users’ index page.

Now, the above does work fine. But if the form’s code is changed to explicitly specify the URL of the ‘create’ action then a ActionController::InvalidAuthenticityToken is thrown when the form is submitted.

<h1>Users#new</h1>
<%= form_for @user, url: users_url do |f| %>
<p><%= f.text_field :name %></p>
<p><%= f.submit %></p>
<% end %>

For me this one worked https://github.com/rails/rails/issues/22965 “proxy_set_header X-Forwarded-Ssl on;”

It seems to be related to this issue https://github.com/heartcombo/devise/issues/5273 where the rails dev:cache appears to solve the invalid token case sometimes (including mine, which were two machines localy and a new setup on Windows/WSL+Ubuntu on this date for a new team member). I’m trying to figure out what the cache system really ‘solves’ 😄

Any thoughts?

Cheers,

I honestly don’t know if my issue is related to this one or not. But its similar.

I am using partial react(partial in a sense that we are not using api but rails actionview and stuff in mix with react, it s migration in progress thats why) on rails 5.1.4 and ruby 2.4.2 with turbolinks enabled.

Issue we are facing is that **a page which is open for say a day and didn’t refresh, on that page there is a bulk delete button, which stops working and raises: ActionController::InvalidAuthenticityToken **(we suspect its because client does nto close the site and uses the same page after a day or two, because otherwise it is working all fine on dev and production)

Of-course we have set protect_from_forgery with: :exception in application controller.

While I prepare a reproduction app here’s the steps:

  1. first view, index, navigate to the object detail, which presents a form.
  2. submit the form and in controller redirect to index
  3. navigate to the form page again, submit, error

in the first fresh request I was able to extrapolate 2 csfr tokens from the page, one of which is in-form:

head: BVMd/Oweu/4r7IIp5WKoYuJZgR5MKqpP34C2Eh1itYYO7wPMgIhMkanTFnJDPiAAsBZ5j0jbPm0qgPuBymGAGA==
form: BVMd/Oweu/4r7IIp5WKoYuJZgR5MKqpP34C2Eh1itYYO7wPMgIhMkanTFnJDPiAAsBZ5j0jbPm0qgPuBymGAGA==

they appear to be the same and have been submitted successfully.

when I navigate to the form page again the tokens are different:

head: 89ofcbytEgoCcA8hk6He2hVH+P1FnGhpONLd4i9uPEL4ZgFB0DvlZYBPm3o1/Va4RwgAbEFt/EvN0pBx+G0J3A==
form: x3ffL4vP5Wqd+CpHW5mzX7px+m05lAXPbIN2q2Z1xni8itZrwp8pfgvMrm7Tk+G7k1fcUnOmu2WSusnfFJzewg==

but when submitting this form a third, unknown token gets submitted instead:

MOoN6HSoPcP4CVgm7XN1gd1D3k8VmVpp2E693pZzVzbkoCNaimgmFEFnlsH/px90uCWa2W0sE2IuRsn6NRva9w

which I verified not being in the page.

I noticed this error only shows up on FF for me, on Chrome it doesn’t throws any error. Is it possible that it has something to do with the browser?. Plus Turbolinks seems to be making two requests but again this only happens on FF and not on chrome.

Started POST "/wishlists" for ::1 at 2017-02-23 11:56:02 +0530
Processing by WishlistsController#create as JSON
  Parameters: {"id"=>"8", "checked"=>"true"}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms)

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

actionpack (5.0.0.1) lib/action_controller/metal/request_forgery_protection.rb:195:in `handle_unverified_request'
actionpack (5.0.0.1) lib/action_controller/metal/request_forgery_protection.rb:223:in `handle_unverified_request'
devise (4.2.0) lib/devise/controllers/helpers.rb:253:in `handle_unverified_request'
actionpack (5.0.0.1) lib/action_controller/metal/request_forgery_protection.rb:218:in `verify_authenticity_token'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:382:in `block in make_lambda'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:169:in `block (2 levels) in halting'
actionpack (5.0.0.1) lib/abstract_controller/callbacks.rb:12:in `block (2 levels) in <module:Callbacks>'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:170:in `block in halting'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:454:in `block in call'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:454:in `each'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:454:in `call'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:101:in `__run_callbacks__'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:750:in `_run_process_action_callbacks'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:90:in `run_callbacks'
actionpack (5.0.0.1) lib/abstract_controller/callbacks.rb:19:in `process_action'
actionpack (5.0.0.1) lib/action_controller/metal/rescue.rb:20:in `process_action'
actionpack (5.0.0.1) lib/action_controller/metal/instrumentation.rb:32:in `block in process_action'
activesupport (5.0.0.1) lib/active_support/notifications.rb:164:in `block in instrument'
activesupport (5.0.0.1) lib/active_support/notifications/instrumenter.rb:21:in `instrument'
activesupport (5.0.0.1) lib/active_support/notifications.rb:164:in `instrument'
actionpack (5.0.0.1) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
actionpack (5.0.0.1) lib/action_controller/metal/params_wrapper.rb:248:in `process_action'
searchkick (1.4.0) lib/searchkick/logging.rb:153:in `process_action'
activerecord (5.0.0.1) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
actionpack (5.0.0.1) lib/abstract_controller/base.rb:126:in `process'
actionview (5.0.0.1) lib/action_view/rendering.rb:30:in `process'
actionpack (5.0.0.1) lib/action_controller/metal.rb:190:in `dispatch'
actionpack (5.0.0.1) lib/action_controller/metal.rb:262:in `dispatch'
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb:50:in `dispatch'
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack (5.0.0.1) lib/action_dispatch/journey/router.rb:39:in `block in serve'
actionpack (5.0.0.1) lib/action_dispatch/journey/router.rb:26:in `each'
actionpack (5.0.0.1) lib/action_dispatch/journey/router.rb:26:in `serve'
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb:725:in `call'
omniauth (1.3.1) lib/omniauth/strategy.rb:186:in `call!'
omniauth (1.3.1) lib/omniauth/strategy.rb:164:in `call'
omniauth (1.3.1) lib/omniauth/strategy.rb:186:in `call!'
omniauth (1.3.1) lib/omniauth/strategy.rb:164:in `call'
warden (1.2.6) lib/warden/manager.rb:35:in `block in call'
warden (1.2.6) lib/warden/manager.rb:34:in `catch'
warden (1.2.6) lib/warden/manager.rb:34:in `call'
rack (2.0.1) lib/rack/etag.rb:25:in `call'
rack (2.0.1) lib/rack/conditional_get.rb:38:in `call'
rack (2.0.1) lib/rack/head.rb:12:in `call'
rack (2.0.1) lib/rack/session/abstract/id.rb:222:in `context'
rack (2.0.1) lib/rack/session/abstract/id.rb:216:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/cookies.rb:613:in `call'
activerecord (5.0.0.1) lib/active_record/migration.rb:552:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/callbacks.rb:38:in `block in call'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:97:in `__run_callbacks__'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:750:in `_run_call_callbacks'
activesupport (5.0.0.1) lib/active_support/callbacks.rb:90:in `run_callbacks'
actionpack (5.0.0.1) lib/action_dispatch/middleware/callbacks.rb:36:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/executor.rb:12:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/remote_ip.rb:79:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/debug_exceptions.rb:49:in `call'
web-console (3.4.0) lib/web_console/middleware.rb:135:in `call_app'
web-console (3.4.0) lib/web_console/middleware.rb:28:in `block in call'
web-console (3.4.0) lib/web_console/middleware.rb:18:in `catch'
web-console (3.4.0) lib/web_console/middleware.rb:18:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
railties (5.0.0.1) lib/rails/rack/logger.rb:36:in `call_app'
railties (5.0.0.1) lib/rails/rack/logger.rb:24:in `block in call'
activesupport (5.0.0.1) lib/active_support/tagged_logging.rb:70:in `block in tagged'
activesupport (5.0.0.1) lib/active_support/tagged_logging.rb:26:in `tagged'
activesupport (5.0.0.1) lib/active_support/tagged_logging.rb:70:in `tagged'
railties (5.0.0.1) lib/rails/rack/logger.rb:24:in `call'
sprockets-rails (3.2.0) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/request_id.rb:24:in `call'
rack (2.0.1) lib/rack/method_override.rb:22:in `call'
rack (2.0.1) lib/rack/runtime.rb:22:in `call'
activesupport (5.0.0.1) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/executor.rb:12:in `call'
actionpack (5.0.0.1) lib/action_dispatch/middleware/static.rb:136:in `call'
rack (2.0.1) lib/rack/sendfile.rb:111:in `call'
railties (5.0.0.1) lib/rails/engine.rb:522:in `call'
puma (3.6.0) lib/puma/configuration.rb:225:in `call'
puma (3.6.0) lib/puma/server.rb:578:in `handle_request'
puma (3.6.0) lib/puma/server.rb:415:in `process_client'
puma (3.6.0) lib/puma/server.rb:275:in `block in run'
puma (3.6.0) lib/puma/thread_pool.rb:116:in `block in spawn_thread'
  Rendering /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb
  Rendering /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.text.erb
  Rendered /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_source.text.erb (1.0ms)
  Rendering /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb
  Rendered /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb (0.8ms)
  Rendering /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb
  Rendered /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb (1.1ms)
  Rendered /Users/abhinay/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/actionpack-5.0.0.1/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb (93.0ms)

Further testing has revealed it only occurs when passing an absolute URL to the :url option, not a relative path. In @elbartoloco’s example app, this works:

<h1>Users#new</h1>
<%= form_for @user, url: users_path do |f| %>
<p><%= f.text_field :name %></p>
<p><%= f.submit %></p>
<% end %>

But this produces the error:

<h1>Users#new</h1>
<%= form_for @user, url: users_url do |f| %>
<p><%= f.text_field :name %></p>
<p><%= f.submit %></p>
<% end %>