lynndylanhurley / lynndylanhurley/devise_token_auth
enable_standard_devise_support causes reset_password process to fail if different user currently logged in
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 3.6k
- Forks
- 1.1k
- PR merge metrics
- No merged PRs in 30d
Description
I am noticing an issue whereby if the server thinks it has a current_user for the browser session then doing a "password reset request=>click the reset link in the email" results in validate token sending back the details for the current user rather than the user doing the reset password.
The problem lies generally in how reset password works (effectively logging in the user based on the reset token) and also how the enable_standard_devise_support works. In set_user_by_token, we have this code...
if DeviseTokenAuth.enable_standard_devise_support
devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
if devise_warden_user && devise_warden_user.tokens[@client_id].nil?
@used_auth_by_token = false
@resource = devise_warden_user
@resource.create_new_auth_token
end
end
This is checking if there is a current_user logged in that didn't come in by this token and if there is then returning it. I'm not quite clear what exactly the point of this is but I know I have the enable_standard_devise_support flag turned on as it fixed an earlier issue I was having (it may well be I should just turn off the flag).
Anyways, in the reset_password scenario, this code is very peculiar. The reset will be sent for a particular user and it doesn't matter if there is a current_user or not, unless maybe if it is the same user as the reset request. What ends up happening where the reset is for a different user is that the set_user_token responds with the details for the current_user instead of the reset request user. I have a check for this type of response on my client side, to deal with an issue with different logins in the same browser session, and it just logs everybody out, screwing up the one-time only reset link.
My gut instinct is that the code to take the already logged in user should check against the passed in uid - I can't see how it makes sense just to respond with the logged in user regardless of the requested uid. So something like this:
if DeviseTokenAuth.enable_standard_devise_support
devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
if devise_warden_user && devise_warden_user.uid == uid && devise_warden_user.tokens[@client_id].nil?
@used_auth_by_token = false
@resource = devise_warden_user
@resource.create_new_auth_token
end
end
However, as I am not too sure why that code was written to not check the uid, an easier way around it is just to do a sign_out before you go into the reset process. This ensures you will never get a logged in user returned. You can overload the passwords controller edit method as follows:
class PasswordsController < DeviseTokenAuth::PasswordsController
def edit
# Stop the current user getting in the way of one time sign in that the reset password process does
sign_out(current_user)
super
end
end
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at set_user_by_token and the enable_standard_devise_support branch shown in the issue, then trace the reset-password edit and validate-token flow. Reproduce the case with one user logged in and a reset request for another user; done means the reset flow returns the requested user's details without invalidating the one-time reset link.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rails, ruby
- Domain
- api, authentication, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100