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-passwordalways returns200 "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
passwordconfig; on failure400with the validation message. - After a successful reset, all of the user's
refresh_jtirecords 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_resethook 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_ttlor implement a per-usertokens_invalidated_atcheck (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).