How password resets work
It's not news that handling user services are a little complicated. I've always had a little trouble with all the moving parts. And if I'm honest, whenever hashes and digests come into play, those long garbled strings muddle up my brain so I can't follow what's going on.
So. Here are the basic steps to resetting a password.
- Render a 'forgot password' page. This is basically the
new
REST action for your password_resets controller. Find the user by the email address the submit through the form. - In the
create
action, create areset_token
(string) andreset_expiry
(date, set to an hour or two in the future) and save them into the appropriate columns in youruser
database. Send the user a URL with thereset_token
as a param. - Create a route to handle the URL you sent the user. This will be your REST
edit
action. In your controller, find the user by searching for both thereset_token
in the URL's params and for thereset_expiry
still being valid (e.g.reset_expiry > Time.now()
). If the user can't be found, flash an error and redirect the user somewhere else. If the database does return a user, render the password reset form. - In your
update
action, search for the user again, the same as in step 3. Change the user's password in the database (and make sure it gets saved!), and then set thereset_token
andreset_expiry
to something falsey (e.g.nil
orundefined
). Log the user in, then flash a success message and redirect the user to the homepage or account page or something.
The only real variation I've seen is in the reset_expiry
. You could, instead of saving a time in the future and searching for a users whose reset_expiry
hasn't expired, save the reset_token_generated_at
instead. Then, in your edit
and update
actions, insert some middleware or a before_action
to check that the reset_token_generated_at
isn't expired.
This solution is arguably more modularized and easy to read. I don't know that modularization would be a huge problem in this case since not a lot of the password reset code can be used elsewhere, but readability is a big bonus. For this reason you'll see this solution employed in Michael Hartl's Rails Tutorial: Ruby is a language that emphasizes readability. Whereas the solution as I've written in in the steps above come from Wes Bos's Learn Node program--Node.js being a somewhat less human-friendly language from a reading perspective.
Previous
The flavour of the now used to be peach; now it's orange. Soon it will be something different.