Files
web/authmail/README.md
T
2026-09-11 08:34:46 -04:00

5.6 KiB

Account mail protocols

authmail provides optional verification, confirmed address changes and password reset. It does not deliver mail directly or own the application router. Use authsqlite.Store.AccountMail for an adapter sharing the identity database and encrypted transactional outbox. Call MigrateMail explicitly and require its independent schema version before enabling these routes. Base identity schema 11 and historical commerce records are unchanged; existing mailboxes start unverified.

Operations

  • Verification requires a current unrestricted account session and confirmation at its existing canonical mailbox. Reading/inspecting a link does not verify it.
  • Address change requires the current password and that session, then separate confirmation at both current and proposed mailboxes. The current address stays authoritative until both confirm. No passkey-approval boolean bypass exists. A future passkey-only path needs a separately bound fresh-approval protocol.
  • Password reset is available only through the current verified mailbox of an active, fully registered account. Request results are generic for unknown, unverified, inactive, malformed and account-throttled addresses. A reset creates no login, removes no passkey/recovery code and bypasses no existing MFA policy.

Tokens use 32 random bytes and purpose-bound SHA-256 digests, expire in 15 minutes, and work once. Requests bind the user ID, canonical address, profile revision and current password digest; address/verification requests also bind the real acting session. A replacement request invalidates the previous link for that purpose. Already-in-flight older mail may still arrive; an invalidated token cannot act.

The SQLite adapter rechecks authority and identity under its writer lock, including fresh time after waits/password hashing. A changed password, profile, address, status or acting session rejects stale requests. Address uniqueness is checked again at final confirmation. Old-address invitations are revoked, not moved; new-address invitations still need normal token/authority checks to be accepted. Existing memberships, ownership and purchase snapshots retain the immutable user.

Successful reset/address changes revoke sessions, pending ceremonies, enrollment and recovery grants, and outstanding account-mail requests. Mailbox changes notify both addresses; resets notify the current mailbox. Account changes, notifications and secret-free audits commit atomically. Delivery capacity/audit failures roll everything back, leaving valid tokens retryable until their original expiry.

Per-account requests are limited to one per minute and five per hour across these purposes. At most one pending request per account/purpose remains. Applications must also impose IP and password-hashing concurrency limits and handle anonymous request responses without disclosing per-account operational failures.

Required application boundaries

  • Use a configured HTTPS origin and fixed route paths. Never build links from a request Host header. Composer customizes reviewed plain-text copy, not security state, sender, recipient, headers or editable executable templates.
  • Render a deliberate confirmation/reset form; only POST consumes a token. Keep strict same-origin/CSRF protection and private/no-store responses. Prefer TokenInFragment so the browser transfers the code into the deliberate POST without placing it in HTTP/proxy request targets. Native forms can require a same-origin referrer policy to retain a usable Origin header; fragments are never included in referrers. Script-only flows can use no-referrer. Do not weaken origin validation to accept opaque/null origins, and never allow link scanners or GET requests to change account state.
  • Exclude tokens, query strings, message bodies, addresses, passwords and SMTP credentials from logs/telemetry. Restrict token-bearing pages and avoid external analytics/resources. Debug redaction does not make structured serialization safe.
  • Return anonymous request responses consistently, independent of eligibility or SMTP acceptance. Run SMTP through the bounded outbox worker, not in the request.
  • After reset/change, clear the browser's old session and return to normal login. Preserve the application's passkey/MFA checks; mailbox control is not an owner recovery grant. Keep printed/owner-assisted recovery separate.
  • Existing Stripe receipts/billing emails are financial snapshots, not canonical login identifiers. Do not rewrite them as part of an account email change.

The design follows the applicable OWASP guidance on password reset and registered-email changes. This is not a claim of a completed application security audit.

Local tests cover both confirmation orders, replay/expiry, generic reset requests, credential/session races, competing resets, address conflicts, stable ownership, retained factors, invitation handling, rate limits, restart and transactional migration/audit/outbox rollback. The account packages and mail/outbox suites pass; focused races and vet pass. The preview28 consumer dogfood also covers native HTTP/UI integration and real BHS inbox verification/reset/both-mailbox flows. Each application must still verify its own sender configuration, recipient delivery and recovery operations; local provider proof is not an assurance of external spam placement or mail reputation.