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...
Related
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
So I have a Rails app with Devise set up. I have :confirmable set up in my Users table, and sending confirmation emails works perfectly.
However, I'm running into a small issue with unconfirmed users trying to sign in.
When an invalid email/password combination are input into the login, I get a flash notice that says "Invalid email or password.". However, if an unconfirmed user signs in correctly, they are redirected back to /users/sign_in, but there is no flash message for "You have to confirm your account before continuing.", which is defined in /config/locales/devise.en.yml.
I have overridden thses methods:
RegistrationsController) :new, :create
SessionsController) :create
ConfirmationsController) :after_confirmation_path_for
What exactly does Devise do when an unconfirmed user signs in with the correct credentials? I tried putting a binding.pry statement at the top of my sessions#create method, but it never hits it, meaning Devise must have some sort of outside check for this. I've attempted to look at the source code to no avail.
This is what the log states is happening:
Started POST "/users/sign_in" for 127.0.0.1 at 2013-10-18 15:04:26 -0400
Processing by SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"03kQgiMGyXcq/nW8jlVyGkGDw1Q9lpP+JZ03e+LZHPU=", "user"=>{"email"=>"dummy#example.com", "password"=>"[FILTERED]"}, "commit"=>"Login"}
[1m[35mUser Load (0.7ms)[0m SELECT `users`.* FROM `users` WHERE `users`.`email` = 'dummy#example.com' LIMIT 1
[1m[36m (0.2ms)[0m [1mBEGIN[0m
[1m[35m (0.1ms)[0m COMMIT
Completed 401 Unauthorized in 90ms
Started GET "/users/sign_in" for 127.0.0.1 at 2013-10-18 15:04:27 -0400
Processing by SessionsController#new as HTML
Rendered devise/shared/_links.haml (0.6ms)
Rendered devise/sessions/new.html.haml within layouts/application (5.0ms)
Rendered layouts/_header.html.haml (0.9ms)
Rendered layouts/_navigation.html.haml (0.6ms)
Rendered layouts/_footer.html.haml (0.9ms)
Completed 200 OK in 33ms (Views: 28.0ms | ActiveRecord: 0.0ms)
So it does look like the sessions#create method is being hit. So I'm not sure where to go from here. Any help would be appreciated!
config/routes.rb
devise_for :users, :controllers => {
:registrations => "registrations",
:sessions => "sessions",
:confirmations => "confirmations"}
Figured it out. When something in devise calls one of the message under :failures, it doesn't put the message in flash[:notice], it puts the message in flash[:alert], so I just needed to add
#alert= alert
to my haml.
Source: Always getting 401 Unauthorized with new install of Rails + Devise
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 an application where users can log in to their firms subdomain.
I use devise. And this code redirects the user form the root domain to the subdomain.
def after_sign_in_path_for(resource_or_scope)
scope = Devise::Mapping.find_scope!(resource_or_scope)
subdomain_name = current_user.firm.subdomain
if current_subdomain.nil?
# logout of root domain and login by token to subdomain
token = Devise.friendly_token
current_user.loginable_token = token
current_user.save
sign_out(current_user)
flash[:notice] = nil
home_path = valid_user_url(token, :subdomain => subdomain_name)
return home_path
else
if subdomain_name != current_subdomain.name
# user not part of current_subdomain
sign_out(current_user)
flash[:notice] = nil
flash[:alert] = "Sorry, invalid user or password for subdomain"
end
end
super
end
It works super in chrome, firefox, opera and safari, but it does not work in IE9. I do not get any error messages. Form the log i see that the user gets sigend in and when the user get redirected to the home page he/she is unauthorized. Has anyone an idea on what is going on? Form the log.
Processing by SessionsController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"JaffZi9f+Uyovuya8wR2u7LjG9w/3wdUDqTqONt/kFM=",
"user"=>{"email
"=>"andreas#lizz.no", "password"=>"[FILTERED]", "remember_me"=>"0"},
"commit"=>"Sign in"}
User Load (0.0ms) SELECT "users".* FROM "users" WHERE
"users"."email" = ''whatever#atlatis.at' LIMIT 1
(0.0ms) begin transaction
(1.0ms) UPDATE "users" SET
"last_sign_in_at" = '2012-03-02 20:46:06.658370',
"current_sign_in_at" = '2012-03-
02 20:56:29.481286', "sign_in_count" = 41,
"updated_at" = '2012-03-02 20:56:29.482286' WHERE "users"."id" = 1
[paperclip] Saving attachments.
(62.0ms) commit transaction
Firm Load (0.0ms) SELECT "firms".* FROM "firms" WHERE "firms"."id" = 1 LIMIT 1
Firm Load (0.0ms) SELECT "firms".* FROM "firms" WHERE "firms"."subdomain" = 'den' LIMIT 1
CACHE (0.0ms) SELECT "firms".* FROM "firms" WHERE "firms"."subdomain" = 'den' LIMIT 1
Redirected to http://den.lvh.me:3000/
Completed 302 Found in 182ms (ActiveRecord: 0.0ms)
Started GET "/" for 127.0.0.1 at 2012-03-02 21:56:29 +0100
Processing by PrivateController#statistics as HTML
Firm Load (0.0ms) SELECT "firms".* FROM "firms" WHERE "firms"."subdomain" = 'den' LIMIT 1
Completed 401 Unauthorized in 2ms
Started GET "/users/sign_in" for 127.0.0.1 at 2012-03-02 21:56:29 +0100
Processing by SessionsController#new as HTML
Rendered devise/_links.erb (2.0ms)
Rendered devise/sessions/new.html.erb within layouts/registration (13.0ms)
Completed 200 OK in 27ms (Views: 26.0ms | ActiveRecord: 0.0ms)
If you are going across subdomain it may be better to simply change your session cookie to be cross-domain.
Editing the session-store.rb file in initializers does this.
Babyreveal::Application.config.session_store :cookie_store,
key: '_babyreveal_session',
:domain => ".mybabyreveal.com"
Notice the . prefix on the domain attribtue. This allows this cookie to be accessible across subdomains and the application should maintain it's session across subdomains. May not be 100% what you are looking for but it should get you going in the right direction.
This is my route:
scope ":username" do
resources :feedbacks
end
So when I go to mydomain.com/test/feedbacks/10 it shows the correct feedback with id=10 that belongs to username=test.
But, if I go to mydomain.com/test2/feedbacks/10 it shows me the same feedback with id=10, which does NOT belong to username=test2.
How do I restrict this from happening?
I am using the Vanity gem to give me the username in the URL, this is what that route looks like:
controller :vanities do
match ':vname' => :show, :via => :get, :constraints => {:vname => /[A-Za-z0-9\-\+\#]+/}
end
Edit 1:
That is to say, for clarity's sake, when I go to mydomain.com/test/feedbacks/10 and /test2/feedbacks/10, it shows me the same view for the same record (in which case, the latter version would be wrong because it should be telling me that no such record exists, but it's not. It is just displaying the correct record for test/feedbacks/10).
Edit 2:
Here are the logs of both requests:
The right request
Started GET "/test-3/feedbacks/7" for 127.0.0.1 at 2011-09-14 02:48:15 -0500
Processing by FeedbacksController#show as HTML
Parameters: {"username"=>"test-3", "id"=>"7"}
Feedback Load (0.5ms) SELECT "feedbacks".* FROM "feedbacks" WHERE "feedbacks"."id" = ? LIMIT 1 [["id", "7"]]
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
Rendered feedbacks/show.html.erb within layouts/application (36.2ms)
Completed 200 OK in 188ms (Views: 184.3ms | ActiveRecord: 1.8ms)
The wrong request
Started GET "/test2/feedbacks/7" for 127.0.0.1 at 2011-09-14 02:48:28 -0500
Processing by FeedbacksController#show as HTML
Parameters: {"username"=>"test2", "id"=>"7"}
Feedback Load (0.1ms) SELECT "feedbacks".* FROM "feedbacks" WHERE "feedbacks"."id" = ? LIMIT 1 [["id", "7"]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
Rendered feedbacks/show.html.erb within layouts/application (37.6ms)
Completed 200 OK in 50ms (Views: 47.5ms | ActiveRecord: 1.2ms)
Your show action should look something like
def show
#user = User.find_by_username(params[:username])
if #user == current_user
...
render "show"
else
flash[:alert] = "Record doesn't exist"
redirect_to root_path
end
end
I took the liberty of adding in #Benoit's suggestion.