The Password Reset Flow
Why password reset matters
Users forget passwords. Without a reset flow, forgotten passwords mean lost accounts. But a poorly implemented reset flow is a security risk: it can be used to take over accounts, enumerate registered emails, or flood users with unwanted emails.
This lesson explains the flow. The next two lessons build and secure it.
The flow
1. User clicks "Forgot password" and enters their email
→ POST /forgot-password { email }
2. Server generates a time-limited, single-use reset token
→ Server hashes the token and stores the hash
→ Server "sends" an email with a link containing the token
3. User clicks the link in the email
→ The link contains the reset token as a URL parameter
→ The user lands on a page where they enter a new password
4. User submits the new password with the reset token
→ POST /reset-password { token, newPassword }
5. Server verifies the token
→ Check: does a matching hash exist?
→ Check: has the token expired?
→ Check: has the token already been used?
6. Server updates the password
→ Hash the new password with bcrypt
→ Replace the old password hash
→ Delete the reset token (single-use)
→ Invalidate all existing sessions Why email
The email step is critical. It proves the person requesting the reset has access to the account’s email address. Without it, anyone who knows your email could reset your password.
The email contains a link like:
https://yourapp.com/reset-password?token=abc123def456 Only the person with access to the email inbox can see this link and use the token.
[!NOTE] In this course, we will “send” the email by logging the reset link to the console. Actual email delivery (via SendGrid, AWS SES, Resend, etc.) is an infrastructure topic outside our scope. The auth logic is the same regardless of how the email is delivered.
Why hash the token
The reset token is stored in your database (or in-memory store). If an attacker breaches your database, they can see the tokens and use them to reset any user’s password.
To prevent this, hash the token before storing it, the same way you hash passwords. Use a fast hash (SHA-256 is fine) instead of bcrypt, because reset tokens are random and high-entropy (no rainbow tables to worry about, no need for slow hashing).
Generated token: "abc123def456" (sent to user via email)
Stored hash: SHA-256("abc123def456") → "9f86d0..." When the user submits the token, hash it and compare the hashes. If the database is breached, the attacker sees hashes, not usable tokens.
Why single-use
After a token is used to reset a password, it is deleted. If the email was intercepted (forwarded, screenshot, shoulder-surfed), the token cannot be reused. The password has already been changed, and the token is gone.
Why time-limited
Reset tokens expire (typically 1 hour). A token that sits in an inbox forever is a liability. If the user’s email is compromised weeks later, an old reset token should not still be valid.
Why invalidate sessions
After a password reset, all existing sessions and tokens for that user should be invalidated. The user who just reset their password gets a fresh session. Anyone else (including an attacker who had access to the old password) is logged out.
Why do we hash the reset token before storing it?
Why are all sessions invalidated after a password reset?