Rails Devise Login Sometimes Does Not Redirect - ruby-on-rails

I have a rails app configured with devise and it works most of the time. Signup seems to have no issues at all.
Sometimes when trying to login an existing user (the same test user I've successfully logged in many times) The page does not redirect.
When I check the logs I see it logging that the redirect was successful. And if I refresh the page then I'm logged in. I haven't been able to reliably recreate the issue but it seems more likely to happen when the server is restarted.
I updated the redirect code to this:
def after_sign_in_path_for(resource)
current_user.sync_with_crm
current_user.save
return stored_location_for(resource) || root_path
end
The current_user.sync_with_crm makes an api call to my CRM and then sets some associations that I need to save on the user model. I don't know how this could be the issue as it does work most of the time.
Not even sure how to trouble shoot this further.
ruby 2.5, rails 5.2.0, devise 4.4.3

Related

Why does devise redirect to current path when session times out

The code here shows that devise will redirect to the currently requested path when the session times out (which is checked and enforced by the timeoutable module) : https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb#L120
The attempted_path is set by warden before invoking the failure app.
Question is : Why would devise redirect back to the current requested path itself? If the session has timed out then shouldn't the client be redirected to the login page for the current entity (User or Admin or whatever)?
It does use the scope_url if attempted_path is not set. But I do not understand why should a redirect be made to the currently requested path again? Wouldn't this just result in a redirect-loop?
This redirect-loop is infact happening with Rails admin. If I enable timeoutable for the model for which I am authenticating in Rails admin, then after session timeout, any request will result in a redirect loop.
So can someone please explain to me why a redirect to attempted_path is being made at all? What use case doe sit serve?
Additional info
Here are the two flows that I have in mind.
How it should be
User tries to access page x. Session is timed out.
User is redirected to the login page
User logs in
User is redirected back to page x
How it is currently
User tries to access page x. Session is timed out.
User is redirected to page x.
And it repeats into a loop until browser says "Website is not redirecting properly".
After a long "debugging weekend" I found out that the issue was because the Session and Cookie middlewares were placed after Warden in the rack stack.
My application is a Rails 5 API application, which means cookies and sessions are not available by default. Despite being an API app, I had to incorporate session / cookie based auth mechanism for certain reasons. So I manually added the two middlewares to the rack stack.
Since I added them in config/application.rb they got added almost at the far end of the stack, i.e. much after the Warden middleware itself. However the Warden middleware makes it very clear that it needs a Session and Cookie manager before it in the stack. That way any session changes it makes will get serialized into the session and the cookie eventually.
This resulted in the session changes done by the failure app being discarded. Because of that the session never got cleared and resulted in a redirect loop. The following steps will make it clearer.
How it should have been
User logs in. Session is set with user id.
User uses. Session is updated with user id.
User idles (at least for timeout period)
User makes a request. Request is sent with same session.
Session is identified as timed out. Clear the session and redirect back to same page.
Browser visits same page again.
No user in the session. Redirect to login page.
How it happened in my case
User logs in. Session is set with user id.
User uses. Session is updated with user id.
User idles (at least for timeout period)
User makes a request. Request is sent with same session.
Session is identified as timed out.
Session is cleared but the cleared session is never serialized back to the cookie. (This happens because in case of auth failure control goes directly back to Warden middleware bypassing all the intermediate middlewares it came through. So it misses the cookie and session middlewares)
Redirect back to same page. Browser keeps the session cookie unaltered.
Browser visits same page again with the same session cookie
Steps 5-8 repeat until browser stops with an error.
Here is a sequence diagram I made capturing the whole flow for anyone interested in the details.
#Prometheous : Thank you for your comment. However one thing is still unclear to me :
In case of a timeout, what issues will be there if the FailureApp directly redirects to scope login url. You say :
Without the redirection to the attempted path, devise wouldn't know
how to redirect to the sign in page.
But, can't it get it from the scope_url method which is used in the else part here : https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb#L128 ?
scope is known for sure.
What am I missing?
My guess is that session is timed out and user is asked to sign in again.
User tries to access page x.
Turns out the user sessions is timed out.
User login again.
User returns to page x.
The timeout login session message is displayed in the action requested.
Glad i could somehow help you! You did an amazing job analyzing the whole devise loop process!
However, i think you are dealing either with a really deep nested bug, or devise isnt setup properly. I tested it on a bigger project by myself and it worked just fine.
In Devise.rb i uncommented:
config.timeout_in = 10.seconds
and changed for testing purposes the timeout to 10 seconds.
I have a FAQ page on this project, which has a page_controller. Inside page_controller i added:
before_action :authenticate_user!
Inside my Devise model, in this case User, i added:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :timeoutable
When i sign in a user, go to the FAQ page, wait for 10 seconds, refresh the page, i get redirected to the sign in page and a message that my session was timed out. When i sign in the user again, i get redirected to the FAQ page, without any additional code than the few steps i showed above.
The flow is (which you probably understand much better than me) like that: Check if user is signed in -> if not, redirect. Now the redirect doesn't have anything to do with the :timeoutable module. The :timeoutable module 'simply' counts down and checks if a user session is valid, if not, well it logouts the user in the background. If the user wants to attempt the page than again, it uses the :authenticate_user! method, checks if the user is signed in, if not, well, redirect him.
It seems like your authenticate_user! is not working the way it should. Have you tried (in development) to update devise?
I highly recommend you to create a super simple app with devise and redo the steps from above and see if it works the way you wanted.
The redirect_url part is normally injected behind the scenes by devise, as far as i know.
I once costumized it for an app, by going to config/initializers/devise.rb
drop inside Devise.setup |config|
require "custom_path"
config.warden do |manager|
manager.failure_app = CustomPath
end
than, in lib/custom_path.rb
class CustomPath < Devise::FailureApp
def redirect_url
## redirect to wherever you want
end
end
thats it. Devise will then redirect to whatever page you want.
Anyways, glad you could still solve the problem by tweaking some parts in the middleware.
Greetings!

Okta stuck in endless loop if no user account in app

I'm building a rails application and have successfully implemented Okta single sign on using the 'devise' gem and 'devise_saml_authenticable' gem. Everything is working great and I am able to successfully log into the app using my Okta account.
The one issue that I am facing is that if a user logs in that has an Okta account, but no account within the application, Otka goes into an endless redirect loop. How would I go about redirecting the user to a login error message or page instead?
Actually, I found out the reason for the issue. It was because, by default, Devise will redirect you back to the login page (http://localhost:3000/users/sign_in) if the user was unable to successfully authenticate (i.e., 401 error). If the user is signed into Okta, when they get redirected to the sign_in path then it tries to do the Okta authentication again, resulting in an endless loop.
To fix this I had to change Devise's default redirect, as instructed by this wiki:
https://github.com/plataformatec/devise/wiki/How-To%3a-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated

Devise session not persisting after login redirect on React-Rails app

We have a pretty standard React-Rails app with (prerender = true) (following this tutorial and his accompanying repo). We're currently using the Devise gem to implement users. However, right now after a successful login and the user is redirected to the home page (as is the default with Devise - we made no changes there) the user session is not persisted, meaning the user is logged out by the time the homepage is reached, according to the user_signed_in? function provided by devise.
Any suggestions?

Devise If logged in automatically send to home page

I have a base installation of Devise with everything setup and working correctly. I have a 'welcome' page and a 'dashboard' page which only logged in users can get to.
What I'm trying to achieve is to make all logged in users automatically skip the landing page and land straight on the dashboard when they come back to the site.
e.g.
I sign up from the landing page
I'm logged in
I close off chrome and go for food
I open chrome and go to the root url (example.com)
I get presented with my dashboard instead of the welcome page as I am logged in.
Any help is greatly appreciated
You should be able to do a simple check in the controller for if the session is active and if so redirect_to dashboard_path
I havent used devise in forever so I don't remember if they have a built in current_user (see the current_user setup here) method or not, but checking if that is nil would be a good check.
You need to create an after sign in devise method in your application controller:
def after_sign_in_path_for(resource)
your_after_sign_in_path
end
That should redirect all users to your_after_sign_in_path after they sign in.

authlogic_facebook_connect not skipping users validation

im running rails 2.3.8, authlogic 2.1.6 and the extension for facebook connect (https://github.com/studybyte/authlogic_facebook_connect)
the facebook side is working good, user click on facebook connect button, goes to facebook, logs in and returns to my app.
my problem is very simple but i cant figure it out. when user is redirected to my site after logging in, if is a new user, i create a new record. Since it comes from facebook the user has no email or password but the user is not saved because authlogic fails password and email validation.
the authlogic_facebook_connect shouldnt skip these validations?
i realized that i don't have to create a user, the plugin is supposed to do that for you. You just have to login the user after he connects to facebook.
It didnt work before because a did not create the facebook_session_key field but facebook_access_token instead.
It should. It's supposed to call save(:validate => false) (in Rails 3.1 or save_with_validation(false) for older version if I remember correctly) to skip the validations.

Resources