Repository navigation
feat(auth): turn off new email-only signups by default - #8492
Conversation
EMAIL_SIGNUPS_DISABLED=true refuses new accounts via web magic link and mobile email code. Existing users keep email sign-in, including OAuth users linking email for the first time; OAuth, passkey, and SSO signups are unaffected.
There was a problem hiding this comment.
bot: Approved. One inline remark is worth fixing before merge: the signup guard can be bypassed for a legacy user whose stored email differs only by case. Two other remarks are informational.
Verified in the head tree:
- The authoritative guard runs after existing-user matching and after the same-email auto-link, and before the Turnstile reuse check, admission, Bouncer, and Stripe. A refusal creates nothing.
- Only two paths create an account with
provider: 'email'(the NextAuth callback andnative/token). Both reach that guard. No other provider is touched. - Existing email users match by auth-provider row. An OAuth user with the same address takes the auto-link branch. An alias match still returns
EMAIL-ALREADY-USED. - Mobile: the OTP route stays enumeration-resistant, and the exchange maps the refusal to
SIGNUP-STEP-UPwith the Google and Apple methods, whichparseAuthErrorandsignupStepUpMessagealready handle. - Web:
callbackAuthErrors,AuthErrorNotification, anduseSignInFlowall accept the new code. The discovery flag appears only onkind: 'new'. - CI: every check except the pending Kilo Code Review is green, and the branch has no conflicts.
Correction: I first posted that the magic-link refusal adds an account-existence signal. That was wrong: /api/sso/organizations already answers new versus existing behind the same Turnstile cookie, so I withdrew that remark.
Not covered: the PR says no manual test, so the new notice has only been rendered in jest. A screenshot would close the Visual Changes item.
Operational note, also in the reviewer notes: with the default off, local dev and preview deployments need EMAIL_SIGNUPS_ENABLED=true, or email signup stops there after this deploys.
| return { | ||
| ok: false, | ||
| status: 403, | ||
| errorCode: 'EMAIL-SIGNUP-DISABLED', |
There was a problem hiding this comment.
bot: Withdrawn as a false positive: no new disclosure. POST /api/sso/organizations already returns kind: 'new' versus kind: 'existing' for any address, behind the same Turnstile cookie, and the sign-in UI relies on that answer. The magic-link refusal therefore reveals nothing that the discovery endpoint does not already reveal. No action needed.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe follow-up commit closes the legacy Assumptions: Files Reviewed (5 files)
Previous Review Summary (commit 004f22a)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 004f22a)Status: 2 Issues Found | Recommendation: Address before merge Overview
Assumptions: The magic-link 403 for new addresses is an accepted account-existence oracle because Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (21 files)
Reviewed by deepseek-flash-latest · Input: 51.1K · Output: 9.5K · Cached: 941.7K Review guidance: REVIEW.md from base branch |
Summary
Turns off new accounts created via plain email (web magic link and mobile email code) by default. Set
EMAIL_SIGNUPS_ENABLED=trueto allow them again. Existing users keep signing in with email, and OAuth (Google, GitHub, GitLab, Apple, Anaconda, LinkedIn, Discord, ChatGPT), passkey, and SSO signups are unaffected.createOrUpdateUserrefusesprovider === 'email'only after existing-user matching and auto-linking, so an existing OAuth user using a magic link for the first time is still linked and signed in. A normalized-email match keeps its existingEMAIL-ALREADY-USEDoutcome. Nothing is created on refusal (no Bouncer call, Stripe customer, or user row). Magic links issued before deploy are also refused for new addresses.checkEmailSignInEligibilityrefuses new users before a magic link is sent; discovery omits Email for new addresses and flagsemailSignupDisabled.EMAIL-SIGNUP-DISABLEDnotice ("Email Sign-up Is Paused … choose one of the other sign-in options. Already have an account? You can still sign in with email."). It appears on the new-account provider screen, after a refused magic-link request (previously any refusal showed "We could not send your magic link"), and on callback redirects. It does not name providers, since the buttons are rendered below it.SIGNUP-STEP-UPwith Google/Apple, which released apps already render.EMAIL_SIGNUPS_ENABLEDmust be exactlytrueto allow email signups; unset or anything else keeps them off.apps/web/.env.testsets it totrueso existing suites keep exercising email signup; new tests turn it off explicitly.Verification
pnpm --filter web typecheck,pnpm --filter web lint,pnpm test:web-env, and targeted jest suites (eligibility incl. default-off values, magic-link, discovery, native OTP/token,src/app/api/auth, AuthErrorNotification, useSignInFlow, signIn callback, auth components, and the full DB-backedsrc/lib/user/index.test.tswith new/existing/OAuth-link/normalized/OAuth-signup cases).Visual Changes
New error notice copy only (existing
AuthErrorNotificationstyling). Screenshots to follow.Reviewer Notes
EMAIL_SIGNUPS_ENABLED=trueis set there.