Commit graph

4 commits

Author SHA1 Message Date
Alexandre Brandizzi
74d5aa17eb fix(auth): trace a reset email the provider rejects as an error
A send that the mail provider did not accept finished its background
transaction as ok, so rejected reset emails looked delivered in tracing.
It now finishes as an error with a fixed message that names no recipient.
2026-09-25 20:01:58 -03:00
Alexandre Brandizzi
de0e767930 fix(auth): refuse a reset email when the queue is full instead of dropping it
The channel used DropWrite, under which TryWrite reports success and
discards the email, so a full queue still counted the request and never
sent the code. Wait makes TryWrite return false when the queue is full,
without blocking, so the request is released and the user can ask again.
2026-09-25 19:38:53 -03:00
Alexandre Brandizzi
a6dd40b972 fix(auth): only count real guesses, drop codes that cannot be emailed, trace reset email sends 2026-09-25 13:12:49 -03:00
Alexandre Brandizzi
77a10e38ca fix(auth): cap reset abuse per account, key code hashes, send reset email off the request path
- Forgot Password is limited to 3 codes an hour and 10 a day per email, and
  an account gets 10 failed code checks a day across every code it is sent,
  so new client addresses and new codes no longer buy more guesses. Refused
  requests answer exactly like accepted ones.
- The reset email is queued to a background sender, and unregistered
  addresses store a row no code can match, so both paths do the same work
  and return without waiting on the mail provider. Each request also clears
  expired codes.
- Code hashes are HMAC-SHA256 under a key derived with HKDF from the JWT
  signing secret; rows in the previous unkeyed format stop matching.
- Email and code are read only from the JSON body.
2026-09-25 13:00:03 -03:00