Skip to content

feat(auth): require verified email before claiming a beta/premium slot#114

Merged
miquelmatoses merged 1 commit into
mainfrom
feat/verify-email-before-beta-grant
Jul 14, 2026
Merged

feat(auth): require verified email before claiming a beta/premium slot#114
miquelmatoses merged 1 commit into
mainfrom
feat/verify-email-before-beta-grant

Conversation

@miquelmatoses

Copy link
Copy Markdown
Collaborator

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:

AND COALESCE((SELECT email_verified FROM auth_users WHERE id = $1), FALSE)

Verified by construction — magic-link & Google via _find_or_create_user:

await conn.execute("UPDATE auth_users SET email_verified = TRUE WHERE id = $1", user["id"])

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_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 preserved.

Tests: verified gate present in both ensure_profile branches + grant truth table (unverified never claims a slot); _find_or_create_user marks verified. Backend 172 passed, frontend 248 passed, build clean.

🤖 Generated with Claude Code

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>
@miquelmatoses miquelmatoses merged commit 368a623 into main Jul 14, 2026
8 checks passed
@miquelmatoses miquelmatoses deleted the feat/verify-email-before-beta-grant branch July 14, 2026 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant