- Signup now creates unverified users and sends a verification email (Resend); dev falls back to [VERIFY-LINK] console log - /api/auth/verify-email: single-use token handler, mints tia_session on success, redirects to /onboarding - /api/auth/resend-verification: rate-limited (3/hr), enumeration-safe - Sign-in gated on email_verified — unverified accounts get 403 with needsVerification flag so the UI can show the resend button - Google OAuth via arctic v3: PKCE + state anti-CSRF, find-or-create user, writes accounts row, mints tia_session - Login page: Google button, check-email screen, resend link on 403 - drizzle/0005_email_verification.sql: creates email_verifications table + backfills all existing users as verified (runs automatically on container start before app boots) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
21 lines
902 B
SQL
21 lines
902 B
SQL
-- Email verification tokens (single-use, 24 h).
|
|
-- Mirrors the password_resets shape already in prod.
|
|
-- Backfill grandfathers all existing users as verified so the Task C
|
|
-- sign-in gate does not lock out accounts created before this migration.
|
|
|
|
CREATE TABLE IF NOT EXISTS email_verifications (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token text NOT NULL UNIQUE,
|
|
expires_at timestamptz NOT NULL,
|
|
used_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE INDEX IF NOT EXISTS email_verifications_token_idx
|
|
ON email_verifications(token);
|
|
--> statement-breakpoint
|
|
-- Grandfather every existing user as verified.
|
|
UPDATE users SET email_verified = now() WHERE email_verified IS NULL;
|
|
--> statement-breakpoint
|
|
GRANT ALL ON email_verifications TO tia_app;
|