"ActionController::InvalidAuthenticityToken" error when using form_with - ruby-on-rails

I have a form that looks like this:
<%= form_with(url: star.starname, method: :post, local: true) do |f| %>
<% star.availabilities.each do |avail| %>
<%= f.label avail.time_slot %>
<%= radio_button_tag(:time_slot, avail.time_slot) %> <br>
<% end %>
<%= f.submit "Create" %>
<% end %>
Immediately after form submission:
Notes:
This is occurring in an app (not an API), so sessions are important, hence CSRF protection must be left on.
The problem occurs in chrome, incognito, and safari.
I have tried logging in with different users and clearing cookies (in case it was being caused by a stale token)
Some more of the error message:
Started POST "/talljohn" for ::1 at 2020-09-16 10:06:21 +1000
Processing by StarsController#book as HTML
Parameters: {"authenticity_token"=>"P++4a+giwUBqZgCLfMwqKpMu0EGitd8zTOi5RWsnxpKlNcjiuU6hd3ebbIC/IOxlL74RJIvrq+yDuA1ZtfcvFw==", "time_slot"=>"2020-09-16 01:00:00 UTC", "commit"=>"Create", "starname"=>"talljohn"}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms | Allocations: 655)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:215:in `handle_unverified_request'
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:247:in `handle_unverified_request'
devise (4.7.2) lib/devise/controllers/helpers.rb:255:in `handle_unverified_request'
actionpack (6.0.3.2) lib/action_controller/metal/request_forgery_protection.rb:242:in `verify_authenticity_token'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:428:in `block in make_lambda'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:200:in `block (2 levels) in halting'
actionpack (6.0.3.2) lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport (6.0.3.2) lib/active_support/callbacks.rb:201:in `block in halting'
Update
I reverted back to the last working version of the form, which was exactly the same as above but without , local: true. Then it suddenly works! (no errors).
I thought local: true (or remote: false) simply turns off ajax form submission. So I don't understand why that would make any difference (or have anything to do with CSRF), it seems that those two aspects are unrelated and it isn't clear why these two concepts would have any affect on eachother
Update 2
I later realised that another untouched previously working form also produced this error. It had not been changed in any way. I tried it in chrome incognito, and it produced the error. Half an hour later (without changing any code) I tried it again in the same browser and it worked. This (very) strange behaviour makes me think it's something to do with sessions, cookies or caching. I will report back if I learn anything further
Update 3
After reading Sarah's solution adding protect_from_forgery prepend: true to the application controller (I tried both before and after before_action :authenticate_user!), the same error message appears in the logs, the POST request isn't actioned, but the app redirects to the home page. I.e. upon POST I see:
Can't verify CSRF token authenticity.
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms | Allocations: 444)
Started GET "/users/sign_in" for ::1 at 2020-09-17 21:08:42 +1000
Processing by Devise::SessionsController#new as HTML
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Redirected to http://localhost:3000/
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 3ms (ActiveRecord: 0.5ms | Allocations: 1900)
Update 4
I attempted to manually clear the rails fragment cache (with Rails.cache.clear ). But the result is exactly the same before/after clearing the fragment cache.

I remember running into something like this and adding protect_from_forgery prepend: true before any user authentication to the ApplicationController solved it.

TL;DR: after 2 weeks' of debugging attempts, I turned off turbolinks and the problem went away.
Aside from turning off turbolinks, another solution appears to be (mentioned here) adding this to application.js
$(document).on('turbolinks:load', function(){ $.rails.refreshCSRFTokens(); });
Previous answer
The issue kept reemerging. I have tried the following six things but it still hasn't fixed it
1. Clear the fragment cache
Rails.cache.clear (warning because it clears the cache, it will also remove things like sidekiq jobs etc). This will remove the stale token and refreshing the app in the browser will return things to normal, and the form should submit (a simple 'resubmit' won't work, so go back to the form page, refresh, then submit and it should work)
2. Hard refresh page
Press cmd + opt + j to bring up the developer console, then right click on refresh and select 'Empty Cache and Hard Reload'
3. Delete site cookies
Right click on the tiny icon to the immediate left of the url (it will be a lock if using https, or the letter 'i' if using http). Go into each of categories listed (e.g. 'Cookies', 'Site Settings' etc) and delete them all
4. Delete cookies for other urls that point to the same site
For example, if your site is www.example.com, and it's hosted on heroku at www.example.herokuapp.com, then delete cookies for that second url as well
5. Delete cookies for localhost
I deleted localhost cookies just to be sure
6. Testing in completely isolated instances of chrome

For me, the solution was to switch to a more specific Cookie Strategy:
# config/initializers/new_framework_defaults_6_1.rb
# Before: i've used None, but this leads to broken Cookies somehow. Strict or Lax seems to work.
Rails.application.config.action_dispatch.cookies_same_site_protection = :strict

Related

Impossible to delete a flash

I made a mistake before migrating a plugin, and have written
flash[:notice] = :label_presta_added
instead of
flash[:notice] = l(:label_presta_added)
I corrected my mistake but it seems that my Redmine Plugin has trashed my Redmine. Even though I delete my plugin a migrate once again, I still get this error:
Started GET "/" for 127.0.0.1 at 2016-06-01 22:21:37 +0200
Processing by WelcomeController#index as HTML
Current user: admin (id=1)
Rendered welcome/index.html.erb within layouts/base (28.1ms)
Completed 500 Internal Server Error in 366ms (ActiveRecord: 116.0ms)
ActionView::Template::Error (undefined method `html_safe' for :label_presta_added:Symbol
Did you mean? html_safe?):
97: <div id="sidebar">
98: <%= yield :sidebar %>
99: <%= view_layouts_base_sidebar_hook_response %>
100: </div>
101:
102: <div id="content">
103: <%= render_flash_messages %>
app/helpers/application_helper.rb:312:in `block in render_flash_messages'
app/helpers/application_helper.rb:311:in `render_flash_messages'
app/views/layouts/base.html.erb:100:in `_app_views_layouts_base_html_erb__4104276684161420982_39604440'
lib/redmine/sudo_mode.rb:63:in `sudo_mode'
Can somebody give me a hand here?
Thanks in advance!
This is stored in your session, so usually changing the session secret key will invalidate all sessions and discard any old session data.
You can also try and rescue to clear it out as a one-time deal.
Have you restarted the server? Or you can use flash[:notice] = nil to remove it.
It looks like it throws a html_safe error. Can you see if the method which is rendering the flash is using html_safe? It looks like its coming from there.
Not sure exactly, may be shooting in the dark.
But read these and try may be:
actionview::template::error(undefined method 'html_safe' for nil:NilClass)
http://www.redmine.org/issues/8477

Rails 4 ActionController::InvalidAuthenticityToken with multiple subdomains

Before let me say that I search google and a lot of posts here at Stackoverflow, but nothing was able to give a solution for my problem. for this, I am creating a new post.
I am creating an application with two sub-domains: account.psantos.dev and app.psantos.dev.
At: config/initializers/session_store.rbI have:
Rails.application.config.session_store :cookie_store, key: '_psantos.co.ao_session', domain: 'psantos.dev'
And I have the following structure folders:
- app
-- controllers
--- app1
---- welcome_controller.rb
--- account
---- welcome_controller.rb
--views
--- layout
---- app1.html.erb
---- account.html.erb
--- app1
---- welcome
----- index.html.erb
--- account
---- welcome
----- index.html.erb
at: app/views/layout/app1.html.erb (http://app1.psantos.dev) I have the following line:
<li><%= "Logout", account_sign_out_url, method: :delete %></li>
When I click this Link (that will go to: http://account.psantos.dev), I got the following error:
ActionController::InvalidAuthenticityToken at /sign_out
ActionController::InvalidAuthenticityToken
on both layout files (app1.html.erb and account.html.erb) I have this line before <\head > tag :
<%= csrf_meta_tags %>
how can I solve this problem?
Update: relevant log
Started GET "/" for 127.0.0.1 at 2015-08-08 12:37:03 +0100 Processing
by APP1::WelcomeController#index as HTML Parameters:
{"subdomain"=>"app1"} Rendered app1/welcome/index.html.erb within
layouts/app1 (0.4ms) [1m[35mEntity Load (0.3ms)[0m SELECT
"entities".* FROM "entities" WHERE "entities"."user_token" = $1 LIMIT
1 [["user_token", "xxxxxxxxxxxxxxxxxxxx"]] Completed 200 OK in 43ms
(Views: 42.0ms | ActiveRecord: 0.3ms)
Started DELETE "/sign_out" for 127.0.0.1 at 2015-08-08 12:37:05 +0100
Processing by Account::SessionsController#destroy as HTML
Parameters: {"subdomain"=>"account"} Can't verify CSRF token
authenticity Completed 422 Unprocessable Entity in 1ms (ActiveRecord:
0.0ms)
ActionController::InvalidAuthenticityToken -
ActionController::InvalidAuthenticityToken: actionpack (4.2.3)
lib/action_controller/metal/request_forgery_protection.rb:181:in
`handle_unverified_request' actionpack (4.2.3)
lib/action_controller/metal/request_forgery_protection.rb:209:in
`handle_unverified_request' actionpack (4.2.3)
lib/action_controller/metal/request_forgery_protection.rb:204:in
`verify_authenticity_token' activesupport (4.2.3)
lib/active_support/callbacks.rb:430:in `block in make_lambda'
activesupport (4.2.3) lib/active_support/callbacks.rb:143:in `block in
halting_and_conditional' activesupport (4.2.3)
lib/active_support/callbacks.rb:502:in `block in call' activesupport
(4.2.3) lib/active_support/callbacks.rb:502:in `call'
Now it's impossible to do :delete request to subdomain.
There is an vulnerability in jquery-ujs and jquery-rails that can be used to bypass CSP protections and allows attackers to send CSRF tokens to attacker domains. This vulnerability has been assigned the CVE identifier CVE-2015-1840. Versions Affected: All. Not affected: Applications which don't use jquery-ujs or jquery-rails. Fixed Versions: jquery-rails versions 4.0.4 and 3.1.3 and jquery-ujs 1.0.4. Impact ------ In the scenario where an attacker might be able to control the href attribute of an anchor tag or the action attribute of a form tag that will trigger a POST action, the attacker can set the href or action to " https://attacker.com" (note the leading space) that will be passed to JQuery, who will see this as a same origin request, and send the user's CSRF token to the attacker domain.
This is commit to jquery-ujs:
You can read more here

Devise warden 401 Unauthorized when wrong credentials

I have a quite standard Devise login procedure with:
View:
resource_name, :url => session_path(resource_name)) do |f| %>
<%= f.input :password, input_html: {class: "span6"} %>
<% if devise_mapping.rememberable? -%>
<p><%= f.check_box :remember_me %> Remember me</p>
<% end -%>
<input type="hidden" name="after_sign_in_page" value="<%=#after_sign_in_page%>">
<p><%= f.submit "Sign in", class: "btn btn-success" %></p>
And I just created a sessioncontroller to downcase the email:
class SessionsController < Devise::SessionsController
def create
params[:user][:email].downcase!
super
logger.debug "Errors: #{resource.errors}"
end
A login with good credentials happens fine.
With wrong credentials, It redirects to the sign-in page with this log:
Started POST "/users/sign_in" for 127.0.0.1 at 2013-01-10 09:59:44 +0100
Processing by SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8eytQkr20JOOOdDvpCWakbmUzNoaHMxK9/BSEVxETik=", "user"=>{"email"=>"nicolas#demoreau.be", "password"=>"[FILTERED]", "remember_me"=>"0"}, "after_sign_in_page"=>"", "commit"=>"Sign in"}
Time zone: (GMT+00:00) UTC, current area: , user to register: , current controller: sessions
Completed 401 Unauthorized in 69ms
Processing by SessionsController#new as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8eytQkr20JOOOdDvpCWakbmUzNoaHMxK9/BSEVxETik=", "user"=>{"email"=>"nicolas#demoreau.be", "password"=>"[FILTERED]", "remember_me"=>"0"}, "after_sign_in_page"=>"", "commit"=>"Sign in"}
Rendered devise/sessions/_new.html.erb (17.8ms)
Rendered devise/sessions/new.html.erb within layouts/application (19.7ms)
Rendered layouts/_header.html.erb (66.1ms)
Completed 200 OK in 173ms (Views: 98.3ms | ActiveRecord: 0.9ms)
Apparently the 401 is dropped by Warden but I couldn't figure out why.
The user is correctly redirected back to the login page but there is no error message displayed (which is normal as they are wiped out by the redirect)
What am I doing wrong?
thanks!
EDIT 1:
For now, I found a quick hack. I added this in SessionsController#new
if params[:user]
flash[:alert] = "Incorrect login or password"
end
Not very elegant but at least, I have something.
First of all, let me advice you against overriding Devise controllers:
In this case, Devise takes care of transforming the email to lower
case for you, so there's really no need to overwrite the create method.
Your app will support Devise updates seamlessly if you stick to the
standard.
Also, Devise should set a flash error automatically, make sure you're displaying it in your view.
The status code 401 is just a standard response for unauthorized requests.
401 Unauthorized is similar to 403 Forbidden, but specifically for use
when authentication is required and has failed or has not yet been
provided
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
You should definitely consider dropping your custom controller,
Cheers
Your flash message is not going to be set because Devise::SessionsController#create calls warden for the authentication which, in case of failures, will call Devise::FailureApp. Your controller never handles the failure scenarios.
If you want a custom message, you can customize the failure app for that, there are some articles in the Devise wiki explaining how to do so.
But in general, you can customize your failure messages via I18n and there is probably a better way to achieve what you want without a need to override Devise controllers.
I agree with jassa, just update your Devise version (with bundle update devise).
Case insensitive emails are already present in Devise, just make sure you have this config:
# devise.rb
Devise.setup do |config|
config.case_insensitive_keys = [:email ]
end
In any case, since you seem to be missing some flash messages and this config, perhaps it would better if you just re-ran the generator:
rails generate devise:install
You should then let Devise overwrite some files, just make sure you backup yours first.

rails 3.1.0 devise with cancan getting unauthorized with invalid username and/or password

I have a fairly simple app using devise and cancan for authentication and authorization. Everything works great except when users try signing in with invalid usernames and/or passwords. When this happens, we get an error loading page with the following exception in the logs:
Started POST "/users/sign_in" for 127.0.0.1 at 2012-02-09 22:23:22 -0600
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"blahblahblahblah", "user"=>{"login"=>"asdasd", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Sign in"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE (lower(username) = 'asdasd' OR lower(email) = 'asdasd') LIMIT 1
Completed 401 Unauthorized in 74ms
I'm not sure what I need to set to allow the authorization and/or how to get more detailed logs to see exactly what is not authorized? If I enter valid credentials I can access the application and all other pieces of the app work as expected.
I know this question has been posted a couple months ago but I hot the same issue, and after fighting it for a few hours, overwriting Devise SessionController, Devise Custom Failure, debugging through, etc.., I finally found out what was going on and I decided to share the solution so you guys won't have to go through that.
The error 'Completed 401 Unauthorized in XXms' happens within the create method of SessionController at the line:
resource = build_resource(...)
And the resource cannot be built since the resource is not passed to the server. Now the problem resides in WHY the resource isn't passed to the server? I'm using JQUERY mobile which post the sign_in as an AJAX call, which JQUERY cannot upload data like that through AJAX.
You need to add to your signin form the data-ajax=false:
in Devise/sessions/new.html.erb modify the form to look like this:
form_for(resource, :as => resource_name, :url => user_session_url, html: {data: {ajax: false}}) do |f|
Hope that helps someone down the road.

Why is the wrong action processing my .ajax request.

This is an issue I have been working around for some time now thinking I would eventually stumble on an explaination. I have not and it's now becoming a little more problematic for me.
I used rails-generate-scaffold to create a simple (users and posts) application. This was done after installing jquery and jquery-ui. I added actions "login" and "auth" to the users controller. Login.html.erb contains some javascript that sends .ajax request to the users#auth action passing the login information (email and password) as parameters.
The the template auth.js.erb exists. The "auth" action responds to format.js. The request looks normal, but rails processes the request with the "show" action rather than the "auth" action.
In other words, a request to userscontrollers#auth (via .ajax) is being processed by userscontroller#show (as JS).
The problem goes away if I remove the "resources :users" route that scaffold added (the correct action is then called). But without this route other useful scaffold stuff becomes unuseable (like users#new).
From Gemfile: gem 'jquery-rails', '>=0.2.6'
Installed jQuery with: rails generate jquery:install --ui
From ../layouts/application.html.erb
<head>
<title>Tab1</title>
<%= stylesheet_link_tag :all %>
<%= stylesheet_link_tag 'jquery-ui-1.8.16.custom.css' %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag 'jquery.min.js' %>
<%= javascript_include_tag 'jquery.min.js', 'jquery-ui.min.js' %>
<%= csrf_meta_tag %>
</head>
Here is ./log/development.log to shows the request and the response.
Started GET "/users/auth/?email=steve&password=[FILTERED]&_=1326063255777" for 24.11.242.181 at 2012-01-08 17:53:45 -0500
Processing by UsersController#show as JS
Parameters: {"email"=>"steve", "password"=>"[FILTERED]", "_"=>"1326063255777", "id"=>"auth"}
^[[1m^[[36mUser Load (0.2ms)^[[0m ^[[1mSELECT `users`.* FROM `users` WHERE `users`.`id` = 0 LIMIT 1^[[0m
Completed 404 Not Found in 6ms
ActiveRecord::RecordNotFound (Couldn't find User with ID=auth):
app/controllers/users_controller.rb:43:in `show'
Suggests that the request --> GET "/users/auth/?email ... is being processed by UsersController#show as JS
Thanks

Resources