feat(auth): require verified email before claiming a beta/premium slot#114
Merged
Merged
Conversation
Closes the disposable-email slot-farming vector on the unverified password -signup path. The "first 500 free Full Moon" grant in ensure_profile is now gated on auth_users.email_verified (migration 032): - ensure_profile: grant SQL gains _EMAIL_VERIFIED_SQL; every caller inherits the gate. Never revokes an existing grant or paid premium (still ORs with current value). - Magic-link and Google (_find_or_create_user) set email_verified = TRUE by construction, so they get the grant on first /me/profile as before. - password_signup: account created unverified, tokens still issued (usable for the free instruments), verification email sent via Resend (reuses the magic_tokens table). New POST /auth/verify-email consumes the token, sets email_verified = TRUE, and re-runs ensure_profile. - Frontend: signup shows the confirm-email card; AuthCallbackPage handles ?type=verify; confirmBody copy updated (6 locales). Backfill (032) marked existing accounts verified — no revocation; the 3 live beta grants are preserved. Tests: verified gate in both ensure_profile branches + truth table; _find_or_create_user marks verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the disposable-email slot-farming vector: the "first 500 free Full Moon" grant now requires a verified email. Depends on migration
032(PR #113, already applied).Grant gate (
ensure_profile,api/main.py) — every caller inherits it, never revokes:Verified by construction — magic-link & Google via
_find_or_create_user:Password signup — created unverified, tokens still issued (free instruments need no account), verification email sent (reuses
magic_tokens). New endpoint:@router.post("/verify-email") # consume token → email_verified = TRUE → re-run ensure_profileFrontend — signup shows the confirm-email card;
AuthCallbackPagehandles?type=verify;confirmBodycopy updated (6 locales).Backfill (032) marked existing accounts verified — no revocation; the 3 live beta grants preserved.
Tests: verified gate present in both
ensure_profilebranches + grant truth table (unverified never claims a slot);_find_or_create_usermarks verified. Backend172 passed, frontend248 passed, build clean.🤖 Generated with Claude Code