Reputation: 1326
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.
And it repeats into a loop until browser says "Website is not redirecting properly".
Upvotes: 3
Views: 1910
Reputation: 889
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!
Upvotes: 1
Reputation: 1326
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.
Warden
middleware bypassing all the intermediate middlewares it came through. So it misses the cookie and session middlewares)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?
Upvotes: 5
Reputation: 91
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.
Upvotes: 0