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.

12
scenarios
9
test cases
12 min
read
intermediate2–4 hours (full suite); 30 min (smoke pass covering happy path + expired token + rate limit) testingQA engineersSDETsSecurity engineers

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 automated

Submit 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 automated

Submit 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 automated

Click 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 automated

Use 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 automated

Complete 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 automated

Submit 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 automated

After 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 automated

After 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 automated

Create 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 automated

Send 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 automated

Navigate 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 automated

Attempt 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. 1.Navigate to /forgot-password
  2. 2.Enter 'reset-test@example.com' and submit
  3. 3.Assert UI shows generic success message ('if this email is registered, you'll receive a link')
  4. 4.Poll test inbox for the reset email (timeout 30 s)
  5. 5.Click the reset link from the email
  6. 6.Assert the password-reset form is displayed (not an error page)
  7. 7.Enter new password 'NewP@ss2024!' and confirm
  8. 8.Submit the form
  9. 9.Assert success message / redirect to login
  10. 10.Attempt login with 'NewP@ss2024!' — assert success
  11. 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

Playwright

End-to-end reset flows, inbox polling via API, multi-session invalidation tests.

Cypress

Network interception to capture reset tokens from API responses; cy.request for API-layer tests.

Postman

API-layer weak-password and rate-limit tests against the reset endpoint.

OWASP ZAP

Automated scan for XSS reflection on the token parameter and enumeration timing vulnerabilities.

k6

Rate-limit burst testing — send a high-frequency burst and assert 429 at the threshold.

Burp Suite

Intercept and replay reset tokens; test for token predictability and parameter tampering.

Mailtrap

Catch and inspect reset emails in staging. Query the inbox API to extract tokens programmatically.