verify / verify (push) Successful in 4m29s
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.
84 lines
5.4 KiB
Markdown
84 lines
5.4 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. Consumer HTTP/UI integration, release/publication and
|
|
controlled real-mail proof remain pending in the repository queue.
|