Trying to sign in a user with Devise, I get an invalid authenticity token error.
I have csrf_meta_tags in my layout, and there is an authenticity_token present in the request params. As is suggested in answers to other questions, protect_from_forgery with: :exception is before before_action :authenticate_user!.
If I comment out protect_from_forgery, I see the following in my server log:
Started POST "/users/sign_in" for 127.0.0.1 at 2018-01-19 16:13:46 -0500
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"[TOKEN]", "user"=>{"badge_number"=>"0285", "password"=>"[FILTERED]"}, "commit"=>"Log in"}
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`badge_number` = '0285' ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) SELECT `divisions`.`id` FROM `divisions` INNER JOIN `divisions_users` ON `divisions`.`id` = `divisions_users`.`division_id` WHERE `divisions_users`.`user_id` = 2
CACHE (0.0ms) SELECT `divisions`.`id` FROM `divisions` INNER JOIN `divisions_users` ON `divisions`.`id` = `divisions_users`.`division_id` WHERE `divisions_users`.`user_id` = 2 [["user_id", 2]]
Redirected to http://localhost:3000/
Completed 302 Found in 141ms (ActiveRecord: 2.1ms)
Started GET "/" for 127.0.0.1 at 2018-01-19 16:13:46 -0500
Processing by IncidentsController#index as HTML
Completed 401 Unauthorized in 0ms (ActiveRecord: 0.0ms)
So it looks like the login is working, but then the redirect to the requested path (/) results in a 401 and being redirected to the login page, as if the login isn't working.
However none of this really explains why I'm getting the authenticity token error in the first place.
Not sure what the next step to investigate the problem might be, given that as far as I can see all the required elements for CSRF token verification are in place.
I fixed this by clearing my cookie for localhost.
I had used SSH tunneling via a localhost port to access the production site, so a cookie based on a conflicting session and secret_key_base was in the localhost domain.
I prevented this from occurring again by prepending the Rails environment to the name of the cookie:
Rails.application.config.session_store :cookie_store, key: "_incidents_#{Rails.env}_session"
So that despite using localhost to access my development and production environments, they would be writing to different cookies by virtue of being in different environments.
For Rails 5, note that protect_from_forgery is no longer prepended to
the before_action chain, so if you have set authenticate_user before
protect_from_forgery, your request will result in "Can't verify CSRF
token authenticity." [link]
To resolve this, either change the order in which you call them, or use :
protect_from_forgery with: :exception, prepend: true
Related
I'm using Rails 4 with Devise. Devise comes with existing forms to set the password so I wanted to add a link for the user to change their own password once logged in.
In my view, I have:
<li><%= link_to 'Change password', edit_user_password_path %></li>
This appears to render the right link:
Change password
When I try to visit this page, I get a flash message saying "You are already signed in", and the logs show:
Started GET "/users/password/edit" for ::1 at 2016-07-05 16:46:17 +1000
Processing by Devise::PasswordsController#edit as HTML
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
Redirected to http://localhost:3000/
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.1ms)
So I can see it hits the right controller and the right action, but then mysteriously redirects.
I don't really understand what this means but I would have thought that the page to edit your password would only work when signed in, so I'm not really sure why I'm getting an error about already being signed in.
Does anyone know Devise well enough to explain what's going on here?
edit_user_password_path is used for forgot password page, you need edit_user_registration_path.
Can you try to use:
edit_user_registration_path
As an authenticated user, if I visit a url to my app via an external text editor, the mimetype shows up in the console as "*/*", and devise seems to redirect to "/sign_in" multiple times. This is a problem because if I go to "/foo", this is initially stored in the session as the path to return to after authentication. Due to the number of redirects, this value is blown away in the session and after devise finally realizes the user is already signed in, it ends up redirecting to "/".
For testing, I added to my application controller:
def authenticate_user!
puts "***** AUTHENTICATE USER CALLED!!!"
super
end
So, if I enter the url directly in my browser, the mimetype shows up as HTML, and it sees I am authenticated immediately and everything is rendered as expected:
Started GET "/products/171" for 127.0.0.1 at 2015-02-11 17:04:53 -0800
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by LayoutsController#show as HTML
Parameters: {"id"=>"171"}
***** AUTHENTICATE USER CALLED!!!
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = 7 ORDER BY "users"."id" ASC LIMIT 1
Rendered html template within layouts/application (0.1ms)
Completed 200 OK in 1165ms (Views: 1127.7ms | ActiveRecord: 3.0ms)
However, when I click a link in an external text editor, I get this crazy behavior:
Started GET "/products/171" for 127.0.0.1 at 2015-02-11 17:09:43 -0800
Processing by LayoutsController#show as */*
Parameters: {"id"=>"171"}
***** AUTHENTICATE USER!!!
Completed 401 Unauthorized in 4ms
I18N keys: [:en, :devise, :failure, :user, :unauthenticated]
I18N keys: [:en, :devise, :failure, :unauthenticated]
=> You need to sign in or sign up before continuing.
Started GET "/sign_in" for 127.0.0.1 at 2015-02-11 17:09:43 -0800
Processing by Devise::SessionsController#new as */*
I18N keys: [:en, :sign_in, :submit_button]
=> Sign in
I18N keys: [:en, :sign_in, :disabled]
=> Your account has been disabled.
Rendered devise/sessions/new.html.haml within layouts/sign_in (6.8ms)
Started GET "/sign_in" for 127.0.0.1 at 2015-02-11 17:09:45 -0800
Processing by Devise::SessionsController#new as HTML
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 7 ORDER BY "users"."id" ASC LIMIT 1
I18N keys: [:en, :devise, :failure, :already_authenticated]
=> You are already signed in.
Redirected to http://localhost:3000/
Filter chain halted as :require_no_authentication rendered or redirected
Completed 302 Found in 5ms (ActiveRecord: 0.5ms)
Started GET "/" for 127.0.0.1 at 2015-02-11 17:09:45 -0800
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 7 ORDER BY "users"."id" ASC LIMIT 1
Processing by LayoutsController#show as HTML
***** AUTHENTICATE USER!!!
Completed 200 OK in 244ms (Views: 178.8ms | ActiveRecord: 0.0ms)
Note the "Redirected to http://localhost:3000/" rather than http://localhost:3000/products/171, which is what should be happening.
Why does */* cause this behavior, and how can I fix this?
UPDATE
Apparently the problem is, I get a completely different sessions between visiting directly from the Chrome's url bar, and from clicking a link in an external text editor (even though the text editor is simply opening a new tab in chrome with that url):
from chrome after clicking link in external text editor:
request.cookies => {"_my_app_session"=>"f32dc5c239fadfc494a775990112f2b5"}
from putting url directly in chrome:
request.cookies => {"_my_app_session"=>"91ac136666b78499ea6e071f89cec7ee"}
How in the world is this possible? It's the same browser.. I thought all tabs in a browser have the same session and there's no way you can change that?
I figured out what is happening here... Microsoft Word is the "external editor" in question, and apparently it visits the url internally before kicking it off to your OS default browser. I guess they are spying on their users to see where they are going and what the response of the site is before sending to a browser. That's why the session is different...
I am trying to create functionality that will allow a remote site to login a User by posting the login credentials through a form to the Devise Session Controller. Instead of having the user enter their credentials through the Devise Login page, they would instead be automatically logged-in.
I am not looking to remotely authenticate an API request and I am not looking to implement an SSO strategy. From the user's perspective, they would be using a different website and a new window would pop-up redirecting to my remote Rails application without having to login.
I have over-ridden the Devise Sessions controller:
class Users::SessionsController < Devise::SessionsController
# Override the action you want here.
protect_from_forgery with: :null_session
def create
puts "Made it in override"
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
puts "After the override method"
end
end
Here are the console results:
Started POST "/users/sign_in" for 127.0.0.1 at 2014-12-24 22:59:54 -0500
ActiveRecord::SchemaMigration Load (0.4ms) SELECT `schema_migrations`.* FROM `schema_migrations`
Processing by Users::SessionsController#create as HTML
Parameters: {"user"=>{"email"=>"user#abc.com", "password"=>"[FILTERED]"}}
Can't verify CSRF token authenticity
Made it in override
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'greg#cronin.com' ORDER BY `users`.`id` ASC LIMIT 1
(0.2ms) BEGIN
SQL (0.6ms) UPDATE `users` SET `current_sign_in_at` = '2014-12-25 03:59:54', `last_sign_in_at` = '2014-12-25 03:54:32', `sign_in_count` = 38, `updated_at` = '2014-12-25 03:59:54' WHERE `users`.`id` = 2
(78.5ms) COMMIT
Redirected to http://localhost:3000/reps/1
After the override method
Completed 302 Found in 261ms (ActiveRecord: 81.0ms)
Started GET "/reps/1" for 127.0.0.1 at 2014-12-24 22:59:55 -0500
Processing by RepsController#show as HTML
Parameters: {"id"=>"1"}
Completed 401 Unauthorized in 9ms
Started GET "/users/sign_in" for 127.0.0.1 at 2014-12-24 22:59:55 -0500
Processing by Users::SessionsController#new as HTML
Rendered devise/shared/_links.erb (1.5ms)
Rendered devise/sessions/new.html.erb within layouts/application (13.8ms)
Completed 200 OK in 123ms (Views: 121.1ms | ActiveRecord: 0.0ms)
The login logic executes and the login credentials are correct, but the redirect returns 401 Unauthorized and causes Devise to prompt for credentials again.
Any idea why the user has not been authenticated??
The cross-site request forgery (csrf) is doing what it's exactly designed to do, prevent users to send requests from outside the website into your pages, note this part in your log
Can't verify CSRF token authenticity
In your controller you find:
protect_from_forgery with: :null_session
which means if there's a violation detected, don't use session data for the current request, so that's why you find that you're not logged in, I don't know how you can overcome it, but you can check devise gem wikis and read how to start a session from another server using the way you want ( other than removing the csrf protection )
You might also want to check this blog post which was linked to from here
I'm using Rails 3.2.8 with Devise 2.1.2.
If a session times out while the user is still in the app, the next time the user clicks something in the app, it redirects to the signin page. However, the value of session[:user_return_to] is often not what I would expect. In trying to track down the cause, I found that Devise (or Warden) is doing two GETs after receiving a 401 error.
For example, if the user is editing a user and clicks on Update after the session timeout, the log looks like this:
Started PUT "/users/9f276de6-2175-11e3-b8ce-01413834ba1d" for 127.0.0.1 at 2013-10-11 16:43:36 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"?", "authenticity_token"=>"+JP3qXGyUEodmmlLQtpOJpxF8JuEg5sp4Iyjit784Ms=", "user"=>{"first_name"=>...}
User Load (1.5ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = '9b5f6b2e-40bc-31e3-b199-0141337836d6' LIMIT 1
Completed 401 Unauthorized in 9ms
Started GET "/users/9f276de6-2175-11e3-b8ce-01413834ba1d" for 127.0.0.1 at 2013-10-11 16:43:36 -0700
Processing by UsersController#show as HTML
Parameters: {"id"=>"9f276de6-2175-11e3-b8ce-01413834ba1d"}
Completed 401 Unauthorized in 1ms
Started GET "/users/sign_in" for 127.0.0.1 at 2013-10-11 16:43:36 -0700
Processing by Devise::SessionsController#new as HTML
Rendered devise/shared/_links.erb (7.2ms)
Rendered devise/sessions/new.html.erb within layouts/application (21.8ms)
Rendered layouts/_header.html.erb (7.5ms)
Rendered layouts/_flash_messages.html.erb (0.1ms)
Rendered layouts/_footer.html.erb (1.3ms)
Completed 200 OK in 174ms (Views: 171.8ms | ActiveRecord: 0.0ms)
Where is that first GET coming from, i.e. how and why is #show getting called after the first authentication failure?
If I could get rid of that first GET, I'm hoping session[:user_return_to] would remain set to the /users/{:id}/edit path, which is probably where the user will want to go after re-authenticating. As it is, session[:user_return_to] is set to /users/{:id} so they don't go back into edit mode when they log back in.
By the way, even if the failing request is a GET rather than a PUT, I still see an extra GET before it goes to the signin page.
So basically this is bug #2421 reported here:
store_location!() method gets wrong location after timeout
which was addressed by fix #2427:
redirect user to the referrer if latest request was not GET after timeout
As best I can tell, that fix wasn't released until Devise 3.0.0.
Summary for Future Reference
Devise 2.1.2 had this routine in device/failure_app.rb:
def redirect_url
if warden_message == :timeout
flash[:timedout] = true
attempted_path || scope_path
else
scope_path
end
end
With that code, the attempted_path was set from the PUT request, so the first redirect tried to do a GET on the URI used by the PUT (in this case, /users/{:id}). When used with a GET, that URI invokes the #show action.
That code has since been updated to:
def redirect_url
if warden_message == :timeout
flash[:timedout] = true
path = if request.get?
attempted_path
else
request.referrer
end
path || scope_path
else
scope_path
end
end
It looks like that will still do a double-redirect on timeout, but for a PUT, it will use the request.referrer (in this case, /users/{:id}/edit), so session[:user_return_to] should get set correctly.
Update October 25, 2013 I've now upgraded to Devise 3.1.1. As expected, a PUT still does two redirects on timeout, but session[:user_return_to] is set correctly so the user goes back to the referrer on signin.
I have this problem with Devise following these actions:
I sign up a new user (works fine)
I confirm the user mail (works fine). At that point I am logged in normally, everything works fine.
Now if I log out and try to log back in I get an unauthorized error (401).
Looking at the server logs here what happens:
Started POST "/users/sign_in" for 127.0.0.1 at 2012-10-26 10:26:23 +0200
Processing by Users::SessionController#create as JSON
Parameters: {"email"=>"olivier.milla#gmail.com", "password"=>"[FILTERED]", "remember_me"=>"0"}
WARNING: Can't verify CSRF token authenticity
User Load (0.0ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'olivier.milla#gmail.com' LIMIT 1
(1.0ms) BEGIN
(0.0ms) COMMIT
(1.0ms) BEGIN
(0.0ms) UPDATE `users` SET `current_sign_in_at` = '2012-10-26 08:26:23', `sign_in_count` = 2, `updated_at` = '2012-10-26 08:26:23' WHERE `users`.`id` = 1
(25.0ms) COMMIT
Rendered devise/sessions/create.json.rabl (1.0ms)
Completed 200 OK in 135ms (Views: 22.0ms | ActiveRecord: 27.0ms)
Started GET "/accounts/new" for 127.0.0.1 at 2012-10-26 10:26:23 +0200
Processing by AccountsController#new as HTML
Completed 401 Unauthorized in 0ms
As you can see, I am logged in, I even get a view rendered (devise/sessions/create.json.rabl) and right after I get redirected to '/accounts/new' where I am not authorized anymore. I can then try to reach any URL I want and keep getting the unauthorized message.
I tried this on a new db (db:reset), I tried cleaning up cookies before log in.
Any idea where this behavior may come from?
I'm using Devise 2.1.2 with Rails 3.2.8.
UPDATE
As requested: AccountsController code:
class AccountsController < ApplicationController
before_filter :authenticate_user!
def :index
#accounts = current_organization.accounts
end
def new
#account = Account.new(:organization => current_organization)
end
def create
#account = Account.new(params[:account])
#account.organization = current_organization
if #account.save
redirect_to :index
else
#TODO
end
end
end
You should do this:
Make sure that you have <%= csrf_meta_tag %> in your layout
Add beforeSend to all the ajax request to set the header like below:
$.ajax({ url: 'YOUR URL HERE',
type: 'POST',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: 'someData=' + someData,
success: function(response) {
$('#someDiv').html(response);
}
});
From: WARNING: Can't verify CSRF token authenticity rails
Althoug this is happening because you enabled :token_authentication in your model.
You may not want this.
Otherwise if you want to skip the authentication just for AJAX requests you may do this:
skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }
Regarding your real error, isn't it because you are checkin for :authenticate_user! user and using the current_organization in your code?
So if it is passing the authentication it will not necessarely have the current_organization value!
Hope it helps!
WARNING: Can't verify CSRF token authenticity is the key. You need to pass csrf token with the ajax request, also make sure that you have csrf_meta_tag in your layout.
WARNING: Can't verify CSRF token authenticity rails can be helpful.