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
newREST action for your password_resets controller. Find the user by the email address the submit through the form. - In the
createaction, create areset_token(string) andreset_expiry(date, set to an hour or two in the future) and save them into the appropriate columns in youruserdatabase. Send the user a URL with thereset_tokenas a param. - Create a route to handle the URL you sent the user. This will be your REST
editaction. In your controller, find the user by searching for both thereset_tokenin the URL's params and for thereset_expirystill 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
updateaction, 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_tokenandreset_expiryto something falsey (e.g.nilorundefined). 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_atinstead. 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.