Password Reset
How "Forgot password?" works, from the request to the email link to the new password, what usually breaks, and how to test reset links, expiry, emails and sessions on mobile and web.
Everyone forgets a password sooner or later: the gym app you haven’t opened since January, the airline account you use twice a year. So they tap Forgot password?, wait for an email and choose a new password. When that works, nobody notices. When it doesn’t, the customer is locked out of their own account, and support gets the call.
Password reset is also a back door into the account. Whoever controls the reset link controls the account, no old password needed. That makes it one of the most security-sensitive flows a beginner tester will touch. The good news: most reset bugs are found by simple, careful observation, and you’ll find six of them yourself in this lesson.
How it works
1 · App
User taps "Forgot password?" and enters an email
2 · App
Same confirmation for every email: "If an account exists, we sent a link"
3 · Email
Link with a long random token, valid once and for a short time
4 · App
Link opens the reset screen, user sets a new password (same rules as sign up)
5 · Server
Password saved, token burned, other sessions signed out, "password changed" email sent
Life of a reset token
becomes invalid forever when it is…
- Used · Password was changed with it
- Expired · Older than the limit (e.g. 30 min)
- Replaced · A newer link was requested (if the spec says so)
- The user asks for a reset. They tap Forgot password? and enter their email (sometimes a phone number).
- The app shows the same confirmation for everyone. Something like “If an account exists for this email, we’ve sent a link.” It must not say whether the email is registered. Otherwise the reset form becomes a tool for account enumerationAccount enumerationFinding out which emails are registered from differences in error messages or responses.Open in glossary →.
- The server creates a token and emails a link. The token is a long random string, stored on the server with an expiry time (often 15 to 60 minutes) and a used flag. The link looks like
https://app.example.com/reset?t=8f2a…. - The user opens the link. On a phone, a deep linkDeep linkA link that opens a specific screen inside an app instead of just its home screen.Open in glossary → should open the app’s reset screen. If the app isn’t installed, the website opens instead.
- The user sets a new password. The same rules as at sign up apply (for example “at least 8 characters”). Some products also refuse the current password, or recently used ones.
- The server finishes the job. It saves the new password (as a hash, see password hashingPassword hashingStoring a one-way scrambled version of a password instead of the password itself.Open in glossary →), marks the token as used, signs out other sessions if the spec says so, and sends a “Your password was changed” email so the real owner notices if it wasn’t them.
Several links: which one works?
Users impatiently tap Send reset link three times, then open the first email. What should happen? There are two common designs:
| Design | What happens | Why teams choose it |
|---|---|---|
| Only the latest link works | Requesting a new link cancels all older ones | Fewer valid keys to the account at the same time. A common choice. |
| All unexpired links work | Any link works until it is used or expires; using one cancels the rest | Friendlier when emails arrive out of order |
Neither is “the” right answer. What matters is that the behavior matches the spec, and that a link never keeps working after a password was successfully changed. If the spec doesn’t say, ask. That question alone often uncovers a missing requirement.
Example requirements
Criterion 7 is rate limitingRate limitingCapping how many requests a user or device can make in a period of time.Open in glossary →: without it, anyone could flood a person’s inbox with reset emails, or hammer the server to test thousands of emails.
What a good reset email looks like
The email is part of the feature, so test it like a screen. Open it, read every word, and check where the link points.
Leaks and wrong environment
FreshCart
Your password
Hi {{first_name}},
You asked for your password. It is: Secret123! You can change it here:
Reset passwordhttp://test-env.freshcart.local/reset?email=anna@test.com
A broken name placeholder, the actual password in plain text (which also suggests the server stores it unhashed), a link to a test environment over plain http, and the email address in the URL.
Safe and clear
FreshCart
Reset your FreshCart password
Hi Anna,
Tap the button to choose a new password. The link expires in 30 minutes and works once.
Reset passwordhttps://freshcart.com/reset?t=8f2a…
Didn't ask for this? Ignore this email. Your password won't change.
Real name, a clear call to action, the expiry time, a random token in an https link to the production domain, and a line for people who didn’t ask for it.
What usually goes wrong
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Account enumeration | “No account with this email” for unknown emails, “Link sent” for real ones | Medium–High (security) |
| 2 | Link never expires | A link from yesterday (or last month) still opens the reset screen | High (security) |
| 3 | Link works twice | After resetting, the same link lets you set another password | High (security) |
| 4 | Old links stay valid | You requested three links and all three work, although the spec says only the latest | Medium–High |
| 5 | Password in the email | “Your password is …” in the email, or the new password repeated in the confirmation | Critical (security) |
| 6 | Wrong environment in the link | Link points to staging. or localhost, or to the website instead of opening the app | High |
| 7 | Other sessions survive | After the reset, the old phone or a stolen laptop is still logged in | High (security) |
| 8 | Password rules not enforced | The reset screen accepts abcd although sign up requires 8 characters | Medium–High |
| 9 | No rate limit | You can trigger 100 reset emails in a minute | Medium (security, cost) |
| 10 | Email problems | Email never arrives, lands in spam, wrong name or language, broken on mobile mail apps | Medium |
| 11 | Deep link problems | Link opens the browser, opens the app’s home screen and loses the token, or crashes when the app was closed | Medium–High |
| 12 | Old password still works | After a successful reset, both the old and the new password log in | Critical |
Find the bug
PulseFit’s reset flow below has six bugs. Under the phone you have a real-feeling inbox, a server clock you can move forward, and a list of Maya’s other devices. Explore like a tester:
- Request a reset for Maya’s email, then for an email that doesn’t exist. Compare the messages.
- Request several links. Open the older one.
- Move the clock forward, then open a link.
- Read every email carefully, including the small print.
- Reset the password and look at the other devices. Try a very short password too.
🐞 Bug hunt: PulseFit password reset
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
PulseFit
Train smarter every day
Server clock
Inbox · maya@test.com
No emails yet.
Maya's other devices
- Maya's iPadPulseFit appSigned in
- Chrome on Macpulsefit.comSigned in
Test account & spec
maya@test.com / Morning5k!
- Messages must not reveal whether an email is registered.
- A link expires after 30 minutes and works once. Requesting a new link cancels older ones.
- Links point to app.pulsefit.com.
- New password: at least 8 characters, different from the current one.
- A reset signs the user out on all other devices.
- Max 5 reset requests per 15 minutes.
- 1
Hint: Request a link for maya@test.com, then for nobody@test.com.
- 2
Hint: Send two reset links, then open the older email.
- 3
Hint: Request a link, tap "+1 hour" on the server clock, then open it.
- 4
Hint: Read the full reset email, including the small "Or open this link" line.
- 5
Hint: Complete a reset, then look at "Maya's other devices".
- 6
Hint: Set the new password to "abcd".
Test scenarios
Functional
- Request a reset for a registered email, receive the email, open the link, set a valid new password.
- Log in with the new password: works. Log in with the old password: rejected.
- A “Your password was changed” email arrives after the reset.
- The email address is trimmed and case-insensitive:
Maya@Test.comworks formaya@test.com. - The reset screen shows which account it’s for (or at least doesn’t let you change someone else’s).
- The back and cancel paths work: leaving the reset screen without saving keeps the old password.
Negative
- Unknown email: same message, same timing, no email sent. Also check the response in DevToolsDevToolsDeveloper tools built into browsers for inspecting pages, network traffic and errors.Open in glossary → or a proxy: the status code and body should look the same too.
- Badly formatted email:
maya@,maya.test.com, empty field. - Expired link: wait (or ask a developer to shorten the expiry on a test environment), then open it.
- Used link: reset once, then open the same link again.
- Older link after requesting a newer one: does it work? Compare with the spec.
- Tampered link: change one character of the token in the URL. It must fail with a friendly message, not a crash or a 500.
- New password that breaks the rules, confirmation that doesn’t match, new password equal to the current one (if required).
Edge cases
- Expiry boundaries: with a 30-minute limit, try at about 29 and 31 minutes. That’s boundary value analysisBoundary value analysisTesting values at and just around the edges of valid ranges, where bugs love to hide.Open in glossary → applied to time.
- Password length boundaries: 7, 8 and 9 characters, the maximum and one more.
- Request a reset while logged in on another device, then use the link.
- Two resets in parallel: open link A in one browser tab and link B in another, submit both.
- The password is changed via Settings (not reset) while a reset link is pending: does the link still work? It usually shouldn’t.
- The account is locked after too many login attempts: does a successful reset unlock it? Check the spec.
- Social login accounts (Sign in with Apple/Google) that have no password: what does reset do for them?
- Unicode and special characters in the new password; a password set on the web must also work in the app.
UI & usability
- The confirmation message tells the user what to do next (“check your inbox and spam folder”).
- Password rules are visible before typing; a show/hide button exists; password managersPassword managerA tool that stores credentials and fills them into login forms, like Apple Passwords, Google Password Manager or 1Password.Open in glossary → offer to save the new password.
- Expired and used links show a clear message with a “Request a new link” button, not a blank page or “Error 400”.
- The email looks right in common mail apps (Gmail, Outlook, Apple Mail) on phone and desktop, in light and dark mode.
- The email is in the account’s language and uses the right name.
Security basics
These are defensive checks you can do with no special skills:
- Generic message for every email (no account enumerationAccount enumerationFinding out which emails are registered from differences in error messages or responses.Open in glossary →).
- Link expires, works once, and older links behave as specified.
- No password in any email, ever. Not the old one, not the new one.
- The token is long and random (not
?user=123or the email address), and the link useshttps. - After the reset, other sessions are signed out (if specified) and the old password stops working everywhere.
- Rate limit on reset requests is enforced on the server (reloading or reinstalling doesn’t reset it).
- The link points to the correct environment and domain.
Accessibility
- Fields have labels, errors are announced by a screen readerScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary →, and the confirmation message is announced too.
- No short time limit on the reset screen itself (the link may expire, but the form shouldn’t time out while someone types).
- The email works with a screen reader: the button is a real link with a meaningful name (“Reset password”, not “Click here”).
Example test cases
TC-RESET-001Reset password with a valid linkpositivehigh
Preconditions
- Account maya@test.com exists
- User is logged out
Steps
- Tap "Forgot password?"
- Enter maya@test.com and tap "Send reset link"
- Open the email and tap "Reset password"
- Enter a valid new password twice and save
- Log in with the new password
- Log out and try the old password
Test data
new password: Sunrise2026!
Expected result
Reset screen opens in the app. The new password logs in. The old password is rejected. A "password changed" email arrives.
TC-RESET-002Same message for registered and unknown emailsnegativehigh
Steps
- Request a reset for maya@test.com
- Note the message
- Request a reset for nobody@test.com
- Compare messages, status codes and response times
Expected result
Both show exactly "If an account exists for this email, we've sent a link to reset your password." Only maya@test.com receives an email.
TC-RESET-003Link expires after 30 minutesedgehigh
Preconditions
- Test environment with a controllable clock, or a reset email that is 31 minutes old
Steps
- Request a reset link
- Open it after 29 minutes (expect it to work, then cancel)
- Open a fresh link after 31 minutes
Expected result
At 29 minutes the reset screen opens. At 31 minutes the app shows "This link has expired" with a button to request a new one.
TC-RESET-004Link cannot be used twicenegativehigh
Steps
- Complete a reset with a link
- Open the same link again
Expected result
The app shows "This link has already been used". The password cannot be changed.
TC-RESET-005Requesting a new link cancels older linksnegativemedium
Steps
- Request link A
- Request link B
- Open link A
Expected result
Link A shows "This link can't be used". Link B works.
TC-RESET-006Reset signs out other devicespositivehigh
Preconditions
- Maya is logged in on an iPad and in a desktop browser
Steps
- Reset the password on the phone
- Use the app on the iPad
- Reload the page in the desktop browser
Expected result
Both the iPad and the browser ask Maya to log in again.
TC-RESET-007Reset email contentpositivemedium
Preconditions
- Account language is German
Steps
- Request a reset
- Open the email on a phone and on desktop
- Long-press or hover over the button to see the URL
Expected result
Email is in German, greets the user by name, contains no password, shows the expiry time, and the link uses https://app.pulsefit.com.
Tip: click a test case to expand it.
Mobile vs web
- Deep links: tap the link in the Mail app and in Gmail. With the app installed it should open the app’s reset screen (iOS Universal Links, Android App Links; see Deep links). Test with the app not installed, closed, in the background, and logged in as someone else. See Interruptions for switching between apps.
- In-app browsers: Gmail and Outlook may open links in their own browser instead of your app. The website fallback must work there too.
- Read the email on the same phone: switching from the app to the mail app and back must not lose what the user typed.
- Password managers: after the reset, iOS Passwords or Google Password Manager should offer to update the saved password.
- Biometrics: if Face ID or fingerprint unlocks a saved password, it must not keep logging in with the old one after a reset.
- Other devices: keep a second phone or tablet logged in and check it gets signed out.
- Open the link in a different browser than the one that requested it, and in a private window. It should still work (the token is in the link, not in a cookie).
- Network tab: in DevToolsDevToolsDeveloper tools built into browsers for inspecting pages, network traffic and errors.Open in glossary →, check the reset request is a
POST, the response doesn’t reveal whether the email exists, and the token never appears in third-party requests (analytics, ads). - Back button: after a successful reset, pressing Back must not let you submit the form again.
- Other tabs: a second tab logged in as the same user should be signed out after the reset (if specified).
- Email preview: desktop mail clients and webmail render HTML differently. Check the button and the plain link both work.
- Cross-browser: the reset page works in Chrome, Safari and Firefox, including autofill of the new password.
Checklist
Password reset checklist
0 of 15 checked
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.Which confirmation message is safest after "Send reset link"?
2.The spec says reset links expire after 30 minutes. Which test best checks this?
3.You requested two reset links. The older one still works. Is it a bug?
4.The reset email says: "Your current password is Morning5k!". What does this tell you?
5.Maya resets her password because her laptop was stolen. What must happen to the laptop session?
Key takeaways
- A reset link is a temporary key to the account: it must be random, expire, work once, and older links must behave exactly as the spec says.
- Show the same message for every email, so the reset form can’t be used to find out who has an account.
- Test the email like a screen: no passwords, right name and language, and a link to the right environment.
- After a reset: the new password works, the old one doesn’t, other sessions are signed out, and a “password changed” email arrives.
- On mobile, check deep links with the app installed, closed and missing; on the web, check other tabs and the Network panel.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡