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

87 lines
5.6 KiB
Markdown

<!-- SPDX-License-Identifier: MPL-2.0 -->
# 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](https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html)
and [registered-email changes](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#changing-a-users-registered-email-address).
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.