Password reset uses hashed single-use tokens, identical responses for unknown accounts, session-family revocation on success, and throttles keyed by account hash plus network range to resist enumeration and flooding.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Password accounts recover access through a purpose-bound, single-use token. Only a digest is stored. Requesting a reset always returns the same public acceptance, whether or not the identifier exists; mail is sent only for known accounts. A later request invalidates prior unused tokens. Completing a reset revokes the user's entire session family and requires a fresh authentication. Throttles apply to a hash of the identifier and to the network range independently, and throttled responses still refuse to disclose account existence.
Scope
- Request, issue, consume, throttle, enumeration resistance, and post-success session revocation for password-accounts.
- Family revoke via the related session-lifecycle package.
Outside this block
- Passwordless magic links, password hashing for the new secret, customer-support impersonation, and SMS delivery architecture.
Contract
- Public request responses are identical for known and unknown identifiers in status, body code, and timing class.
- Stored verifiers are digests; the raw token appears only in the out-of-band message and is never logged.
- Consumption is atomic from issued to consumed and cannot succeed twice.
- Successful consumption revokes the session family defined by the related session block and does not keep the old cookie valid.
- Throttle keys are account-hash and network-range; hitting either budget returns RESET_THROTTLED with the same enumeration-safe body.
- Issuing a new token marks previously issued unused tokens for that purpose as invalid.
Implementation guidance
- Normalize identifiers before hashing throttle keys when the account system treats them as equal. Bind tokens to purpose password-reset so a login token cannot be reused here.
- Perform password update, token consume, and session-family revoke in one transaction. Do not send no-account mail.
Failure handling and safeguards
- Expired, consumed, and unknown tokens show the same recovery action. If consume succeeds and session revoke partially fails, retry revoke; do not undo the password change.
- Attacker-controlled redirect URLs are rejected; destination is an allowlisted app route captured at issue time.
Verification and operations
- Compare known versus unknown request traces for timing class and body. Consume a token twice and assert a single password change. Prove sibling sessions die after success.
- Alert on throttle saturation by range, which may indicate distributed enumeration.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.