feat(auth): email verification on registration #78
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/email-verification"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds an email-verification flow for standalone deployments built on the existing password-reset SMTP plumbing. Registration now sends a one-time verification link to the address the user typed; the same link is re-issued whenever they change their email under /settings, and a Resend button covers the case where the original message goes missing.
Verification tokens are 32 random bytes URL-base64-encoded, stored in auth.db as a SHA-256 digest with a 24-hour TTL (longer than the password-reset window because users routinely sit on welcome mail for a day or more). Each row pins the address the token was issued for; on click the server only marks email_verified_at when the user's current email still matches that address, so a token issued against the old value cannot silently verify a new one after an in-flight change. After a successful verification every outstanding token for the user is burned, so a forwarded link sitting in another inbox stops working.
set_user_email now clears email_verified_at in the same UPDATE statement when the address actually changes (COALESCE compare against the stored value), so re-saving the same email on the profile form is a true no-op. The /settings handler snapshots the prior value, invalidates outstanding tokens, and spawns a fresh dispatch only when the address transitioned. Dispatch is fire-and-forget on a tokio task so a slow or unreachable SMTP relay never gates the user-visible response, matching the existing password-reset behaviour.
The settings page renders a Verified or Unverified badge next to the email field whenever SMTP is configured, with a Resend Verification Email button that POSTs to /verify-email/resend and redirects back with a generic flash. The verify endpoint returns a small standalone result page (success or expired/invalidated/unknown) that works whether or not the visitor is signed in, so emailed links open from any device. When SMTP is not configured the verify routes return 404 and the badge does not render, mirroring the /forgot + /reset 404 pattern.
Covered by a new db_email_verification.rs integration suite: round-trip and single-use, unknown tokens, invalidate_all_for_user, the mark_email_verified email-match guard, and the resave vs. genuine-change behaviour of set_user_email.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com