Reviewed source export adds verified TLS mail, encrypted outbox, mailbox verification and password reset protocols. Preserve public ancestry; omit local development history and operational queue. Consumer deployment and inbox delivery proof remain separate.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<!-- 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. Consumer HTTP/UI integration, release/publication and
|
||||
controlled real-mail proof remain pending in the repository queue.
|
||||
Reference in New Issue
Block a user