This is yet another "How is a forgot my password page done right" question. However, it explicitly addresses two implementation options that I have come across recently.
The common approaches [1,2] that I know are based on a random token that can only be used once and that has a limited validity:
- User visits forgot password page
- User provides email address (or similar)
- If the email is associated with a valid user account
- The app generates a random, unique token t. t is of sufficient entropy (say 128 Bit).
- The app stores the random token t and the timestamp it was created on alongside the user in the database
- The app sends an email containing a password reset link to the users' email address. The link looks something like this: https://foo.bar/resetPassword?token=t
- User clicks on the link in the email
- The app checks
- if there is a token t inside the database for a given user
- if yes, checks that it is still valid based on the stored timestamp
- if yes, allows the user to access the resetPassword page
- The user sets a new password
- After that, the app invalidates the token t
You can add a little more security stuff like out of band channels or security questions. But for me, that is the common best practise that I am aware of.
Now consider a different approach. We will loose the single use property. However, the benefit is that the app does not have to keep track of the token states at all.
Instead of generating a random token, we will use an HMAC as follows:
- token = HMAC_k(email, timestamp)
k is a secret that the app knows of sufficient entropy. The reset link which is emailed to the user then looks as follows:
When the user accesses the link, the app verifies it as follows:
- calculate HMAC based on URL parameters: token_calculated = HMAC_k(email, timestamp)
- check if the token provided as URL parameter and the calculated HMAC match
- if yes, check if token is still valid based on the timestamp
- if yes, allow the user to access the resetPassword page
In my opinion, the property of not having to maintain a server-side state is more valueable than the single use property. This assumes that a reasonable, short validity of tokens is configured at the server-side - say 20 to 30 minutes.
I would really like to know what other people think of the second approach. Especially, if there are any drawbacks that I might have not noticed yet.