-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
current_user should not perform sign in by default #4951
Comments
@vincentwoo can you provide a code example of how to reproduce the current behavior in this issue? |
I can later but this snippet from the devise codebase explains the behavior: https://github.com/plataformatec/devise/blob/715192a7709a4c02127afb067e66230061b82cf2/lib/devise/controllers/helpers.rb#L60 |
Okay, so let's say you have a SessionsController with the goal to prevent logins conditionally: class SessionsController < Devise::SessionsController
def create
user = User.find_by_email(sign_in_params[:email])
if user.some_condition?
return redirect_to some_other_url
end
super # otherwise login as usual
end
end If, say, the view for |
I agree. This made it more challenging to implement two-factor auth. We want to redirect to the two-factor auth page after checking the user's password, but some other code (like session tracking) checks if there is a |
@brendancarney what did you do as a workaround? |
@vincentwoo This may seem obvious to you, but, why would you call class SessionsController < Devise::SessionsController
def create
user = User.find_by_email(sign_in_params[:email])
if user.some_condition?
sign_out_and_redirect current_user
end
super # otherwise login as usual
end
end and handle where to send them in |
Because you want to conditionally show a different view to logged in users who mistakenly end up on that page through an old link from email, slack, etc. |
Ok I see how that might propose a challenge. But for security reasons, if you don't know how or if the user has properly authenticated (i.e. example you've provided) this seems to be an insecure design approach. You still have referrer which might help you handle where to route the users if they are still properly authenticated. It still seems to me that |
Your statement gives me the impression that you may be confused - current_user's explicit purpose is to tell you whether or not the user is signed in. I am saying that it should NOT be a secret, double purpose to perform a login if one is not found, which it does now. Application code everywhere in the app must rely on current_user (or equivalent helper, user_signed_in?) in order to decide whether the user is authorized to view the current page, etc. Logins should only explicitly be created in controller code, not automatically by helpers. |
That behavior is very surprising, and I spent a significant amount of time debugging an issue because of that |
I understand this be may problematic in some cases, but how do you propose to solve the problem? The user should only have an active session if they have actually done a @vincentwoo in your case, it might make sense to expire sessions after a certain amount of inactivity, or when your application is expiring pages/links as you've described. |
Easy enough. Probably you would have current_user stop calling warden.authenticate, and use the more appropriate warden method, |
That may seem logical, however it is clearly a departure from the
So generally speaking it seems clear that according to devise docs you should not call |
Respectfully, that is silly - Tutorials like this one will commonly contain (apparently mistaken) refrains like:
Even more simply, though, the whole design here is somewhat crazy. These helpers are defined by Devise:
Of these, both |
@lacostenycoder we ended up adding a custom warden strategy. Looks something like:
|
won't it be better to capture the 2FA code in the same form as the password? |
@brendancarney which gem-extension do you use for 2FA? |
@krtschmr I can't answer for brendan but we're using https://github.com/tinfoil/devise-two-factor with no problems. |
@vincentwoo @lacostenycoder @brendancarney @shaicoleman @krtschmr please have a look on this #5017 |
Hi everyone, thanks for all points raised in this discussion. First of all, I want to say that I would also prefer an explicit behavior rather than an implicit one, which is what's happening in There is something that should be addressed though: the As for the use cases mentioned before, I think overriding the sessions controller is not the best alternative. The two-factor auth, for example, is another way of authenticating a user, so a Warden strategy is the best way to solve this problem, as you mentioned already here before. That being said I think it's better to close this issue and continue with #5013 where we are going to fix |
I don't follow your reasoning. Why is memoization the relevant pattern here? The problem isn't that |
I'm not sure. I've seen it both ways. It seems like most of the services I use have you authenticate with username and password before getting to 2FA.
We use https://github.com/twilio/authy-devise along with the custom warden strategy I mentioned in a previous comment. |
@vincentwoo My point is that it's commonly used in methods that have side-effects. That's why I think that as long it's documented and another version (that doesn't have this side-effect) is available, it's ok to keep it as it is. |
Okay. Are you going to add a version of |
should be either something like the question is: is |
@vincentwoo The version that I mean is |
The boolean method alone is not very helpful for porting code to correct it. I'd rather have a version of current_user that does not login, but I suppose I could add it myself as a helper. |
Since the code is out there for almost 10 years we can't know the impact a change like that would have, that's why we rather not change this behavior, even in a major version (we don't want to make it a pain for people to update it). I'm going to close this then and |
we just add functionality and we won't break anything |
@krtschmr That also adds a complexity to the method that I don't think it's justified. All of the use cases showed here in this thread could be achieved using alternative APIs that are already available. |
had a problem today. |
I don't know whether i'm on the same page, but I had this problem with my custom strategies. I don't where to get help. tons of google or perhaps misleading guide. My view:
My strategies :
My controller :
its calling authenticate! method and its breaking it. 😢 |
@abigoroth I'm not sure your problem is the same as the above. By the error message, it seems like |
What if this functionality was added as a setting in |
Glad I found this. Using devise I have some controllers which require sign in, but others that don't require it, but if the user is signed in, the UI reflects that. (imagine the user prefs in the nav bar) Between this and the documentation, I couldn't figure the idiomatic way to do this. What works for me is a before action below. What is the right way to do this?
|
Faced this same problem today when working on custom implementation of 2FA. I understand @tegon's point on backwards compatibility, that's indeed too big of a change nowadays. But I do feel such implicit sign-in is not quite right either. So I ended up adding this simple instance method override logic to alias_method :original_current_user, :current_user
def current_user
original_current_user if warden.authenticated?(scope: :user)
end This way it omits implicit sign-in while keeping all the existing logic intact. Hope that helps! |
Faced the same problem that the Tried workaround from @pokrovskyy (here), but i got this error instead:
So I ended up having this workaround instead def current_user
super if warden.authenticated?(scope: :user)
end |
Pretty disappointed in the outcome of this issue..... First off for those looking: @frullah's solution does work. I put it in the This "solution" @tegon came to is very misleading to developers. Take this documentation for example: So as you can see, misleading documentation, misleading method names, actually led to a vulnerability in our auth. @tegon Why not default the behavior to NOT sign in the user "magically" and instead provide a config option for those older code bases? This would both be forward thinking but also keeps in mind those older codebases. Like this: Devise.setup do |config|
config.sign_in_when_calling_current = true
end |
Environment
Current behavior
It is very hard to prevent sign in with Devise. If you subclass SessionsController to override create, even if you don't allow super to run, any template code that tries to check
current_user
will automatically run warden's authentication, which signs in the user.Expected behavior
Users should only be signed in after an invocation of
sign_in
.The text was updated successfully, but these errors were encountered: