HOW TO TEST
How to Test Password Reset.
Authentication A complete testing guide for password-reset flows: link delivery, token expiry and reuse prevention, new-password validation, old-password invalidation, session management, rate limiting, and error-message privacy.
Password reset is one of the most security-sensitive flows in any application. A flaw here can hand attackers a free credential-reset mechanism: predictable tokens let them take over any account; missing expiry means a token remains valid indefinitely; over-verbose error messages enumerate which emails are registered. This guide covers the complete reset surface — requesting a reset, email delivery, reset link validation, token expiry and single-use enforcement, new-password strength rules, old-password invalidation, session termination post-reset, rate limiting, and error-message privacy. All test cases are written for a Cypress/Playwright automation engineer.
Risks
Predictable or weak reset tokens
Reset tokens derived from timestamps, user IDs, or sequential counters can be guessed. An attacker who knows or can infer any of these values can generate a valid token for any account without access to the email.
Tokens that never expire
A reset link that does not expire remains valid indefinitely. An attacker who gains access to an old email thread containing a reset link can use it to take over the account years later.
Token reuse — reset link valid after one use
If the token is not invalidated upon first use, an attacker who captures the reset URL (via Referer header, browser history, or server log) can reuse it to reset the password again without receiving the email.
Active sessions not invalidated after password change
If existing sessions remain valid after a password reset, an attacker who had access to the old session (e.g. via a compromised device) retains access even after the legitimate user resets their password.
Account enumeration via reset request response
Returning different messages for registered vs unregistered emails ('we sent you a reset link' vs 'no account found') reveals which email addresses have accounts, enabling targeted attacks.
No rate limiting on reset requests
Without rate limiting, an attacker can flood a victim's email inbox with reset emails, causing email fatigue, or probe the system to enumerate registered emails via timing differences.
New password not validated server-side
If password-strength rules are enforced only via frontend JavaScript, a direct POST to the password-reset API can set a trivially weak or empty password, bypassing all UI-level controls.
Test Scenarios
Reset request for a registered email sends a link
CriticalfunctionalFully automatedSubmit a reset request for a known registered email. Assert a reset email is delivered, contains a reset link, and the UI shows a generic success message.
Reset request for an unregistered email shows a generic message
CriticalsecurityFully automatedSubmit a reset request for an email that has no account. Assert the UI message is IDENTICAL to the registered-email response — no hint whether the email exists. No email should be sent.
Reset link from email opens a functional reset form
CriticalfunctionalFully automatedClick the reset link from the email. Assert it opens the password-reset form (not a generic error), the form accepts a valid new password, and submitting it updates the credential.
Expired reset token is rejected
CriticalnegativeFully automatedUse an expired reset token (advance server time or force-expire in DB). Assert a user-friendly 'link has expired' message and a 'request a new link' option.
Reset token is invalidated after first use
CriticalsecurityFully automatedComplete a successful password reset. Attempt to navigate to the same reset URL a second time. Assert the token is rejected and the user cannot reset the password again without a new email.
New password must meet strength rules at both UI and API layers
HighnegativeFully automatedSubmit weak passwords (too short, no complexity) via the UI and via direct API POST. Both must reject. Confirm the server validates independently of the frontend.
Login succeeds with new password after reset
CriticalfunctionalFully automatedAfter a successful reset, attempt login with the new password. Assert it succeeds and redirects to the authenticated area.
Old password no longer works after reset
CriticalsecurityFully automatedAfter a successful reset, attempt login with the old password. Assert it is rejected with an authentication error.
All existing sessions are terminated after password reset
HighsecurityFully automatedCreate multiple active sessions (e.g. log in on two devices/browsers). Complete a password reset on one. Assert that sessions on other devices are invalidated and receive a 401 on next request.
Reset request endpoint is rate-limited
HighsecurityFully automatedSend rapid successive reset requests for the same (or different) email addresses via the API. Assert 429 Too Many Requests is returned after the threshold.
Tampered or random token is rejected
HighsecurityFully automatedNavigate to /reset-password?token=randomgarbage. Assert a clear invalid-link message, not a 500 error or any data disclosure about the user.
Resetting to the same current password — policy decision
MediumnegativeFully automatedAttempt to reset the password to the same value as the current password. Assert the application either (a) rejects it with a 'must differ' message (recommended) or (b) accepts it silently. Document which behaviour is intended.
Detailed Test Cases
Preconditions
- Test account exists: reset-test@example.com / OldPass1!
- Test inbox accessible via API (Mailhog, Mailtrap)
Steps
- 1.Navigate to /forgot-password
- 2.Enter 'reset-test@example.com' and submit
- 3.Assert UI shows generic success message ('if this email is registered, you'll receive a link')
- 4.Poll test inbox for the reset email (timeout 30 s)
- 5.Click the reset link from the email
- 6.Assert the password-reset form is displayed (not an error page)
- 7.Enter new password 'NewP@ss2024!' and confirm
- 8.Submit the form
- 9.Assert success message / redirect to login
- 10.Attempt login with 'NewP@ss2024!' — assert success
- 11.Attempt login with old password 'OldPass1!' — assert rejection
Expected result
New password works; old password is rejected; reset completes without errors.
Test data
- email: reset-test@example.com
- old password: OldPass1!
- new password: NewP@ss2024!
Edge Cases
Multiple active reset tokens for the same account
A user requests a reset link, doesn't use it, then requests another. Both links are in their inbox. The application should define which token is valid: ideally, issuing a new token invalidates all previous ones for that account.
Reset link opened in a different device or browser
Reset tokens are stateless and should work regardless of which device or browser opens the link, since the user may request on mobile and open on desktop.
Password reset while already logged in
Some applications show the forgot-password form to authenticated users. The reset should still work correctly and terminate existing sessions, even the one currently making the request.
Unicode or special characters in the new password
Passwords containing emoji, CJK characters, or characters that encode differently in UTF-8 vs UTF-16 can break hashing if the server does not normalise the string before bcrypt processing.
Reset link with extra query parameters appended by email clients
Email clients (especially corporate ones) sometimes append tracking parameters to links. If the server validates the entire URL including query string rather than just extracting the token, valid links can fail.
Reset link clicked while user is mid-registration
If a reset email is triggered while the user's account is in an unverified state, the reset flow should either reject the request or handle the state transition gracefully.
Password reset for a deactivated or locked account
Some systems disallow password resets for locked accounts to prevent bypass of the lockout mechanism. Others allow it to help users regain access. The behaviour should be intentional and tested.
Reset with a password identical to the current one
If the policy disallows reusing the current password, the system must detect this even when the password is hashed — requiring a bcrypt compare at reset time, not a plaintext comparison.
Long or whitespace-only new password
A password of 1,000 spaces passes a minimum-length check if the validator doesn't trim first. bcrypt on a 1,000-character string silently truncates to 72 bytes — potential mismatch at login if login trims but reset doesn't.
Reset email delivered to spam — user retries repeatedly
If a user can't find the reset email and keeps clicking 'resend', each resend invalidates the previous token. But excessive retries with short delays may exhaust the rate limit.
Automation Ideas
Token expiry via DB hook in test environment
Use a test-only API endpoint or direct DB query to force-expire a token immediately after capture. Eliminates any sleep() in tests and makes expiry scenarios deterministic.
Tools: playwright, cypress
Email enumeration timing assertion
Record response times for registered vs unregistered reset requests and fail the test if the delta exceeds 200 ms. Guards against timing-based enumeration regressions.
Tools: playwright, k6
Multi-session invalidation test
Programmatically create two authenticated sessions via the API, trigger a reset from one, then assert the other receives 401. Runs in CI without any manual browser switching.
Tools: playwright, rest-assured
Weak-password API matrix
Drive a table of {token, newPassword, expectedStatus} through the reset API, covering empty, too-short, no-uppercase, and no-symbol cases. Assert none result in a password change.
Tools: postman, playwright, rest-assured
Rate-limit burst test
Send N+1 reset requests in a tight loop and assert the 429 appears within the expected window. Tag this as 'rate-limit' suite so it doesn't run on every PR build.
Tools: k6, playwright
Reflected-XSS scan on token parameter
Fuzz the token query parameter with a set of XSS payloads and assert none are reflected unsanitised in the HTML response. Combine with a CSP-violation listener.
Tools: owasp-zap, playwright
Common Bugs
Reset link works even after account is deleted
If the token validation query joins on the users table by user ID but doesn't check for account existence first, a reset link issued before deletion can still be used to reset a non-existent account's password.
Response time difference leaks registration status
The registered-email path hashes the token and writes to the DB; the unregistered path returns early. The 50–200 ms timing difference is measurable and reveals which emails have accounts.
Impact: Enables email enumeration at scale without any per-request error to detect.
Token embedded in the HTTP Referer header
The reset URL (containing the token) can appear in the browser's Referer header when the user clicks a link on the reset-confirmation page. Third-party analytics or image requests on that page capture the token.
Old password still accepted immediately after reset
Password hashing and session invalidation are processed asynchronously. In a distributed system, an in-flight authentication request with the old password can succeed for a brief window after the reset completes.
Multiple concurrent resets generate conflicting tokens
Two simultaneous reset requests create two token records. Both remain valid. Whichever is used last wins, but the user may receive only the first email — the second token they try is already consumed.
Reset form pre-fills password from browser autofill
Password managers autofill both 'new password' and 'confirm password' fields with the user's saved (old) password, causing the user to unknowingly reset to their current password without noticing.
Generic 500 error on expired token instead of user-friendly message
The token lookup throws a database exception (not-found, null pointer) that is not caught, resulting in a generic 500 error page rather than a helpful 'link has expired, click here to request a new one' message.
Useful Tools
End-to-end reset flows, inbox polling via API, multi-session invalidation tests.
Network interception to capture reset tokens from API responses; cy.request for API-layer tests.
API-layer weak-password and rate-limit tests against the reset endpoint.
Automated scan for XSS reflection on the token parameter and enumeration timing vulnerabilities.
Rate-limit burst testing — send a high-frequency burst and assert 429 at the threshold.
Intercept and replay reset tokens; test for token predictability and parameter tampering.
Catch and inspect reset emails in staging. Query the inbox API to extract tokens programmatically.
// Related resources