devise user sign_in gives authentication error for

2019-01-14 22:30发布

问题:

I am using devise (latest version - 3.2.0) with rails (latest version - 4.0.1)

I'm doing simple authentication (without ajax or api) and getting an error for CSRF authenticity token. Check the POST request below

started POST "/users/sign_in" for 127.0.0.1 at 2013-11-08 19:48:49 +0530
Processing by Devise::SessionsController#create as HTML

 Parameters: {"utf8"=>"✓",    
 "authenticity_token"=>"SJnGhXXUXjncnPhCdg3muV2GYCA8CX2LVFV78pqddD4=", "user"=> 
{"email"=>"a@a.com", "password"=>"[FILTERED]", "remember_me"=>"0"},
"commit"=>"Sign in"}

Can't verify CSRF token authenticity
 User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."email" =  
'a@a.com' LIMIT 1
(0.1ms)  begin transaction
SQL (0.4ms)  UPDATE "users" SET "last_sign_in_at" = ?, "current_sign_in_at" = ?,
"sign_in_count" = ?, "updated_at" = ? WHERE "users"."id" = 2  [["last_sign_in_at", Fri,
08 Nov 2013 14:13:56 UTC +00:00], ["current_sign_in_at", Fri, 08 Nov 2013 14:18:49 UTC
+00:00], ["sign_in_count", 3], ["updated_at", Fri, 08 Nov 2013 14:18:49 UTC +00:00]]
(143.6ms)  commit transaction
Redirected to http://localhost:3000/
Completed 302 Found in 239ms (ActiveRecord: 144.5ms | Search: 0.0ms)

The root url points to home#new, which is like

class HomeController < ApplicationController
   before_action :authenticate_user!
   def index
   end
end

Sign_in page generated html view is like:

  • meta tags

    <meta content="authenticity_token" name="csrf-param" />
    <meta content="aV+d7Z55XBJF2VtyL8V3zupR3OwhaQ6UHNtlQLBQf5Y=" name="csrf-token" />
    
  • form

    <form accept-charset="UTF-8" action="/users/sign_in" class="new_user" id="new_user" 
    method="post">
    <div style="margin:0;padding:0;display:inline">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="authenticity_token" type="hidden
    value="aV+d7Z55XBJF2VtyL8V3zupR3OwhaQ6UHNtlQLBQf5Y=" />
    </div>
    <div><label for="user_email">Email</label><br />
    <input autofocus="autofocus" id="user_email" name="user[email]" type="email" value="" />
    </div>
    
    <div><label for="user_password">Password</label><br />
    <input id="user_password" name="user[password]" type="password" /></div>
    
    <div><input name="user[remember_me]" type="hidden" value="0" />
    <input id="user_remember_me" name="user[remember_me]" type="checkbox" value="1" /> 
    <label for="user_remember_me">Remember me</label></div>
    
    <div><input name="commit" type="submit" value="Sign in" /></div>
    </form>
    

The authentication request even updates the last_sign_in_at and sign_in_count values, but when I try to access current_user in controller it comes as nil.

According to me it is not actually signing in user. But then question comes "why it is updating last_sign_in_at/sign_in_count value in the user table ?"

回答1:

When the CSRF token is not correct, Rails will not read or do any updates to the session. It will still do any other actions the controller specifies, which explains why you see the DB update but don't end up logged in.

I notice that the authenticity_token in your log and in your page do not match. I realize this just may be because you captured a different request, but you should check that the one on the page matches the one in the log for the same request. I'm also assuming that you are using the proper tag in your view which generates a different authenticity_token each time, and are not just hard-coding the HTML input.

Do you see the same issue with forms unrelated to devise? If not, as a workaround, you can exempt your action from the CSRF check with the following code in your controller:

protect_from_forgery except: :sign_in

The odds of a CSRF attack against your sign in action seems pretty remote (<-pun) to me.

Also, if it's just with devise, then I'd suggest you file an issue with them, which you've already done.



回答2:

There was no problem with devise gem . I removed 'rails-api' gem and my app started working.



回答3:

Add this on the Application Controller (this works on Rails 5)

protect_from_forgery unless: -> { request.format.json? }

Add this to the Devise SessionController

skip_before_action :verify_authenticity_token, only: [:create]

Try it with curl

curl -X POST -v -H 'Content-Type: application/json' http://0.0.0.0:3000/users/sign_in -d '{"user" : {"email": "name@mail.com", "password": "secret" }}'


回答4:

I fixed the same issue just removing "remote: true" option in view: changed form_for(model, remote: true) to form_for(model)