1Step-by-Step Breakdown
The Password Reset Flow's Core Steps. A standard, secure reset flow: the user requests a reset with their email, the server generates a single-use, time-limited token and emails a reset link containing it, the user clicks the link and submits a new password, and the server validates the token before accepting the change — each step has specific security requirements.
Never Confirming Whether an Email Exists. A "reset requested" response should be IDENTICAL regardless of whether the submitted email actually corresponds to a real account — returning a different message for "email not found" versus "reset email sent" lets an attacker enumerate valid accounts by testing which emails produce which response.
Generating a Cryptographically Random, Single-Use Token. A reset token must be unguessable — generated via a cryptographically secure random source, not a predictable value like a sequential ID or a timestamp — and marked as consumed (invalidated) the instant it's successfully used, preventing the same reset link from being usable more than once.
Storing the Token Hashed, Not Plaintext. The reset token, like a password, should be stored hashed in the database — if the database is ever compromised, a plaintext reset token would let an attacker immediately reset any user's password whose token hadn't yet expired, exactly as dangerous as a leaked plaintext password.
A Short, Enforced Expiration Window. A reset token valid for days or weeks significantly widens the window an attacker who intercepts the reset email (via a compromised inbox, a shared computer) has to exploit it — a short expiration (typically 15 minutes to 1 hour) balances legitimate user convenience against this exposure window.
Invalidating All Sessions After a Successful Reset. Following the same principle covered in Session Management, a successful password reset should invalidate every existing active session for that account — if the reset was necessary because of a suspected compromise, this ensures any session an attacker had already established is also terminated.
Rate Limiting the Reset Request Endpoint. Without rate limiting, an attacker could trigger a flood of reset emails to a victim's inbox (a form of harassment/denial-of-service) or use the endpoint's response timing to attempt email enumeration despite an identical response message — rate limiting the request endpoint itself closes both of these residual risks.
Why should a password reset endpoint return an identical response regardless of whether the submitted email actually corresponds to a real account?
- →A different response for "email not found" vs. "reset sent" would let an attacker enumerate which emails correspond to real accounts
- →An identical response is required purely to keep the endpoint's response time consistent
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Clear, Consistent Password Reset Flows Reduce Confusion for Users Recovering Account Access
A password reset flow with clear, consistent messaging and a reasonable expiration window helps every user successfully recover access to their account, particularly important for a user relying on assistive technology who may need more time to complete the multi-step reset process before a token expires.
SEO Implications
- 1
A Secure Password Reset Flow Prevents Both Account Enumeration and Account Takeover Vulnerabilities
A properly secured password reset flow closes off both an account enumeration vulnerability (revealing which emails are registered) and an account takeover vulnerability (a guessable or leaked reset token) — either of which, if exploited at scale, could lead to a serious security incident damaging platform trust.
Best Practices
Return an identical response from the reset request endpoint regardless of whether the submitted email corresponds to a real account
This prevents an attacker from using the endpoint's response to enumerate which email addresses are registered on the platform.
Store reset tokens hashed with a short, strictly enforced expiration, and invalidate all sessions upon a successful reset
Hashing protects the token if the database is compromised, a short expiration limits the exposure window, and full session invalidation ensures a reset performed due to a suspected compromise actually terminates any access an attacker had already gained.
Frequent Bugs
An attacker is found to have been able to determine which email addresses are registered accounts on the platform by observing different responses from the password reset request endpoint.
This is a classic account enumeration vulnerability caused by the reset endpoint returning distinguishable responses based on whether the submitted email exists. Unify the response to be identical in both cases, regardless of the actual account status.
Real-World Examples
Closing an Account Enumeration Vulnerability Found in a Bug Bounty Report
A bug bounty researcher reported that a company's password reset endpoint returned a distinctly different, faster response for a non-existent email ("email not found," returned almost instantly) compared to a registered email ("reset link sent," which took noticeably longer due to actually generating a token and queuing an email) — allowing systematic enumeration of registered accounts through both the message content and the response timing difference. The fix unified both the response message and, critically, the response timing (always performing the token-generation work, or an equivalent-duration no-op, regardless of whether the account existed) to make the two cases genuinely indistinguishable from the outside.
// Both message AND timing made indistinguishable
await (userExists ? generateAndSendResetToken(email) : simulateEquivalentDelay());
res.json({ message: "If that email exists, a reset link has been sent" });