โ† Pattern library

Authentication

Password Reset

Lets users recover access when they forget their password, via a time-limited email link to set a new one.

๐ŸŒฑ

When to use this

Required for any app that uses password authentication. Without it, forgotten passwords mean lost users.

authpasswordresetrecoveryemail
โœจ Built using these library patterns:
password-reset

What I assumed

I made these guesses to fill gaps. Let me know if any are wrong.

    Flow diagram

    Step-by-step recipe

    Copy this and paste into Cursor, Claude Code, or v0.

    PATTERN: Password Reset
    INPUT: email (in request), new_password (in reset)
    OUTPUT: password_updated | error_message
    
    STEPS:
      1. User clicks "Forgot password?" on sign-in screen
      2. User enters email address
      3. ALWAYS show "If that email exists, we've sent a reset link" โ€” never confirm/deny existence (security)
      4. IF email exists in DB โ†’ generate one-time signed token (1h expiry)
      5. Send reset email with link containing token
      6. User clicks link in email
      7. Server validates token: not expired, not used, hash matches
      8. IF invalid โ†’ show "This link expired. Request a new one?"
      9. Show new password form with strength meter
      10. User enters new password (min 8 chars, ideally with strength check)
      11. Hash and store new password (bcrypt/argon2)
      12. Mark token used, invalidate ALL existing sessions for this user
      13. Send confirmation email "Your password was changed"
      14. Redirect to sign-in with success toast
    
    ERROR_HANDLING:
      - Email service down โ†’ log + show "Try again shortly", don't expose service status
      - Token expired โ†’ friendly "Link expired, request a new one" with CTA
      - User submits weak password โ†’ show specific guidance (length, complexity)
      - Concurrent reset requests โ†’ invalidate previous tokens, only latest valid
    
    EXTENSION_POINTS:
      - 2FA challenge after reset (composable_with: ["2fa"])
      - Notify on suspicious reset (different IP/country) (composable_with: ["security-notification"])
      - Compromise check via HaveIBeenPwned (composable_with: ["password-breach-check"])
    

    States โ€” how things change

    StateDescriptionTransitions
    Awaiting email entryUser on 'forgot password' form
    • Email submittedโ†’Reset email sent
    Reset email sentUser waiting for email link
    • Link clickedโ†’Validating token
    Validating tokenServer checking reset link validity
    • Validโ†’Setting new password
    • Expired or usedโ†’Awaiting email entry
    Setting new passwordUser entering new password with strength check
    • Password savedโ†’Complete
    CompletePassword updated, all sessions invalidatedterminal

    Easy-to-miss situations

    The kinds of edge cases that break demos.

    • What if attacker submits common emails to enumerate accounts?

      high

      Different responses for "exists" vs "doesn't exist" leak account presence.

      Suggested handling: Always return same message regardless of email existence. Don't include "we couldn't find that email" anywhere. Add rate limiting per IP.

    • What if user resets password while logged in on other devices?

      high

      Old sessions still valid โ€” attacker holding old session keeps access.

      Suggested handling: After password change, INVALIDATE all sessions for this user (force re-login everywhere). Show "Signed out from N devices" confirmation.

    • What if reset link is clicked from a previewer (Slack, email scanner)?

      medium

      Link gets "consumed" (marked used) before user even sees it.

      Suggested handling: Don't mark token used on first GET. Only mark used when user submits new password. Use POST for state change, not GET.

    • What if user uses an extremely common password (123456)?

      medium

      Account immediately vulnerable, defeats purpose of reset.

      Suggested handling: Reject top 1000 most common passwords (use HaveIBeenPwned API or local list). Show "This password is too common โ€” try a unique phrase."

    • What if the email gets delayed by hours?

      medium

      Token expires before user receives it.

      Suggested handling: Use 1-hour expiry (forgiving). Track email delivery time and alert ops if > 5 min average. On expired clicks, instantly offer "Send a new one" with one click.

    Composes well with

    Combine these patterns when you need a richer flow.

    Build a flow starting from this pattern โ†’