Skip to content

Password reset

Password reset uses one-time tokens of type "password_reset" stored in token_adapter. Requires token_adapter and (optionally) an email_transport.

Flow

sequenceDiagram
    participant U as User
    participant A as App
    participant T as TokenAdapter
    U->>A: POST /auth/forgot-password {email}
    A->>T: store hash(reset_token), TTL 30m
    A-->>U: 200 "If the email exists, a reset link has been sent"
    Note over A: Always returns 200 (do not leak which emails exist)
    A-->>U: email with link {base_url}/auth/reset-password?token=...
    U->>A: POST /auth/reset-password {token, new_password}
    A->>T: consume token (atomic)
    A->>A: validate password, hash, store
    A->>T: delete all refresh_jti tokens for user (force re-auth everywhere)
    A-->>U: 200 "Password reset successfully"

Endpoints

curl -X POST http://localhost:8000/auth/forgot-password \
  -H "Content-Type: application/json" \
  -d '{"email":"a@b.com"}'

curl -X POST http://localhost:8000/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{"token":"...","new_password":"n3w-pw!"}'

Behavior details

  • /auth/forgot-password always returns 200 "If the email exists, a reset link has been sent". It does not reveal whether the email is registered.
  • Reset tokens expire after 30 minutes (hard-coded).
  • The new password is validated against password config; on failure 400 with the validation message.
  • After a successful reset, all of the user's refresh_jti records are deleted, forcing every existing session to re-authenticate. Access tokens remain valid until they expire (15 min default) — there is no access-token blocklist by default.
  • on_password_reset hook fires after the password is stored.

Common mistakes

  • Not gating access tokens after reset — access tokens issued before the reset remain valid until expiry. If you need immediate invalidation, shorten access_token_ttl or implement a per-user tokens_invalidated_at check (FastAuth does not provide this out of the box).
  • Storing the raw token in your DB — FastAuth never stores the raw token, only sha256(raw).