Password-reset QA · an account-recovery fire drill
Run the Recovery Fire Drill: How to Test Password Reset Before a Locked-Out User Has To
Test password reset end to end: request privacy, reset email and link, expiry, one-time use, repeated requests, password policy, old credentials, existing sessions, and final recovery.
Password reset is the emergency exit of authentication. Most users ignore it until their normal credential no longer gets them in, which is why a polished reset email is weak evidence. A useful test starts before the request, follows the recovery credential through the inbox, changes the password, and then stays for the aftermath: can the old password still log in, can the same reset action be accepted twice, how does a newer request affect an older one, and what happens to sessions that existed before recovery?
Quick answer: how do you test a password reset flow end to end?
Begin with a dedicated test account whose old password and active-session state are known. Request recovery through the public forgot-password form, match the resulting email to that request, inspect and use the intended reset link, set a valid new password, then prove the new password works and the old one does not. After the happy path, test unknown-account privacy, expiry, repeated use, repeated reset requests, and existing-session behavior as separate recovery cases.
Before pulling the alarm, write down the account state
| Baseline | Record before reset | Why you need it later |
|---|---|---|
| Credential | A known old password for the dedicated test account | Lets you prove the reset actually replaced the credential |
| Recovery address | The exact inbox controlled by the test | Separates this recovery attempt from another identity |
| Account state | The starting state required by your product | Prevents another account-state defect from masquerading as reset failure |
| Session A | A signed-in session created before recovery | Lets you test what existing sessions do after password change |
| Environment | The authorized staging or test target | Lets you detect a reset URL that crosses environments |
Question 1 — should “forgot password” reveal whether an email has an account?
This is one of the most useful negative cases because it happens before any email arrives. In an authorized test environment, submit the reset form once with a known test account and once with an unregistered test address. Compare the public behavior your product intends to keep consistent, including the user-facing message and response shape.
OWASP recommends a consistent message for existent and non-existent accounts and consistent response timing, alongside protections against excessive automated reset submissions. QA should therefore test the public request boundary, not merely assert that the known account eventually receives mail.
The recovery drill — walk one valid request all the way home
- 1
Request one reset and timestamp the attempt
Trigger recovery once for the dedicated account. Keep the request boundary clean before introducing another request.
- 2
Match the reset email to that request
Check recipient, sender, subject, arrival window, and environment. Do not select a link merely because it is the newest reset-looking message in a reused mailbox.
- 3
Inspect the link without publishing the credential
Confirm HTTPS, expected host, and reset route. Preserve sanitized diagnostics instead of copying a still-valid full token into long-lived logs or tickets.
- 4
Open the public reset flow and choose a valid new password
Exercise the user-facing recovery path and apply the same password policy the application promises elsewhere.
- 5
Prove the credential transition
After success, authenticate with the new password and separately prove the old password no longer authenticates the account.
- 6
Inspect the aftermath
Return to the session created before recovery, check any security notification the product promises, and verify the post-reset session policy explicitly.
Question 2 — what should happen when a password-reset link expires?
| Moment | Assertion | Do not confuse it with |
|---|---|---|
| Inside validity window | A current valid reset action can reach the password-change step | Email copy merely claiming the link is valid |
| After server-side expiry | The reset action cannot change the password | A frontend countdown reaching zero |
| Expired-link screen | The user gets a safe route to request recovery again | A generic server error that happens to block the reset |
| After rejection | Account credential and protected state remain unchanged | Assuming rejection text proves no state mutation occurred |
Test the expiry boundary, not just the countdown
Expiry belongs to the server-side recovery contract. In a controlled test environment, prefer a supported way to shorten the reset lifetime or control test time rather than adding a long real-time wait to CI. The assertion is simple: a recovery action valid before the boundary must be rejected after the configured boundary without changing the account.
Keep the UI in the test when it communicates expiry. A technically correct rejection that strands a locked-out user with no way to request a fresh email is still a recovery defect.
Question 3 — can a password-reset link be used twice?
| Attempt | What to do | What it proves |
|---|---|---|
| First use | Use the valid link and set password B | The current recovery credential can complete its intended transition |
| Second use | Open the same reset action again after success | Whether a consumed recovery credential follows the one-time-use policy |
| Credential check | Try password B and the previous password separately | Which credential owns the account after recovery |
| State check | Confirm the second use did not produce another password change | A rejected-looking page did not hide an unexpected mutation |
OWASP's reset-token guidance maps directly to QA
OWASP recommends reset tokens or codes that are random, sufficiently long, securely stored, single use, and expired after an appropriate period. It also recommends that the account not be changed until a valid token is presented. Those properties become expiry, second-use, invalid-input, and no-premature-state-change tests.
Question 4 — should an old reset link work after requesting a new one?
Do not answer this from habit. Create reset request A and keep its link unused, then create request B. Test A and B deliberately and compare the results with the product's documented supersession policy. QA should prove the chosen policy is explicit and deterministic rather than borrowing a rule from another system.
This scenario also catches an inbox-testing trap: message B can arrive before message A. Delivery order does not tell you which reset request is newer. Keep request timestamps or another safe correlation boundary so the test knows which recovery action it is exercising.
Question 5 — which forgot-password test cases belong beyond the link?
| Case | What should be asserted | Bug it can reveal |
|---|---|---|
| Weak new password | The normal password policy is enforced during recovery | Reset path bypasses credential policy |
| Mismatched confirmation | No password change occurs | Client and server validation disagree |
| Invalid reset action | Safe rejection with unchanged account state | Recovery validation defect |
| Account binding | A recovery action changes only its intended test account | Identity association defect |
| Wrong environment | Reset action stays within the intended environment | Staging email points to production or vice versa |
| Repeated reset requests | Request controls follow documented product policy | Forgot-password endpoint creates uncontrolled notification noise |
| Security notification | Notification is sent when promised and contains no live reset secret | Recovery event happens silently or exposes sensitive material |
Question 6 — what happens to existing sessions after a password reset?
This question needs a session created before the reset. If you test only in the same browser that performs recovery, navigation during the reset can destroy the evidence you needed. Keep a separate pre-reset session and revisit it after the password changes.
OWASP recommends either offering the user a way to invalidate existing sessions or invalidating them automatically. Your product may choose a specific policy, but QA should make that choice visible: does the pre-reset session remain authenticated, get revoked immediately, or require another event? Record the expected behavior and test it instead of assuming password change automatically means session revocation.
OWASP also advises against automatically logging the user in after reset because doing so adds authentication and session-handling complexity. If a product intentionally chooses different behavior, that transition deserves its own explicit security review and test.
When recovery fails, locate the broken stage before filing the bug
| Symptom | Inspect first | Useful evidence |
|---|---|---|
| Form says success but no mail arrives | Request handling, outbound mail, routing | Account fixture, request time, environment |
| Mail arrives with wrong host | Reset URL generation and environment config | Sanitized hostname and route |
| Link opens but password cannot be saved | Recovery validation, password policy, reset state | Validation response and account state |
| New password works and old password also works | Credential update or authentication data source | Separate login results after reset |
| Same link is accepted again | One-time-use handling | First-use and second-use results |
| Reset succeeds but old session survives unexpectedly | Session policy and session store | Independent pre-reset session result |
| Known and unknown accounts respond differently | Forgot-password request privacy | Sanitized response comparison |
Release gate: evidence a password-reset test should leave behind
- Known starting account, old credential, recovery address, environment, and an independent pre-reset session.
- Known-account and unknown-account request behavior compared according to account-privacy policy.
- Reset message tied to the intended request with sender, recipient, subject, and arrival window.
- HTTPS host and route verified without needlessly persisting the live reset credential.
- Valid reset completes only after valid recovery credential presentation.
- New password authenticates and old password no longer does.
- Expired, repeated-use, invalid, and repeated-request cases follow documented policy.
- Existing-session behavior after reset is explicitly asserted.
- Recovery or security notification is checked when the product promises one.
A password-reset test should feel less like checking an email template and more like rehearsing an emergency. Start with a known account, prove the request protects account privacy, use the recovery credential once, replace the password, then inspect everything the reset leaves behind. The flow is healthy only when the right user can recover the right account and earlier credentials, recovery actions, and sessions behave exactly as the product's security policy says they should.
Need a clean recipient for manual email QA?
Use MailOnce to inspect real signup, OTP, magic-link, password-reset, and notification messages without putting a personal inbox into the test fixture.
Create temporary emailSources & further reading
Primary security and test-runner documentation used to ground the testing guidance.
Continue testing
Move to the next boundary in the same email-testing problem space.