From 6de541848c29313daff475f5253df29ac0ed89fd Mon Sep 17 00:00:00 2001 From: ramadhan sjamsani Date: Mon, 27 Apr 2026 13:43:37 +0800 Subject: [PATCH] Phase 3.4: customers.display_name nullable + identity-only social scope Drop NOT NULL on customers.display_name so phone-OTP and social signups can land before the user picks a name; frontend then routes them to /auth/set-name. Google sign-in no longer requests the name claim and Apple SDK scope is trimmed to email only. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/src/db/migrate.js | 4 ++++ backend/src/services/auth.service.js | 6 ++++-- backend/src/services/social-identity.service.js | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/backend/src/db/migrate.js b/backend/src/db/migrate.js index a2881b2..e8b6165 100644 --- a/backend/src/db/migrate.js +++ b/backend/src/db/migrate.js @@ -352,6 +352,10 @@ const migrate = async () => { await sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_customers_google_sub ON customers (google_sub) WHERE google_sub IS NOT NULL` await sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_customers_apple_sub ON customers (apple_sub) WHERE apple_sub IS NOT NULL` + // display_name is set after sign-in via the set-display-name screen for + // direct phone/Google/Apple sign-ups (no anonymous bootstrap). Allow null. + await sql`ALTER TABLE customers ALTER COLUMN display_name DROP NOT NULL` + // Control center users: password-based auth columns // firebase_uid stays for backward compat during migration; will be dropped in a later cleanup migration await sql`ALTER TABLE control_center_users ALTER COLUMN firebase_uid DROP NOT NULL` diff --git a/backend/src/services/auth.service.js b/backend/src/services/auth.service.js index 25836a8..3c6b2b3 100644 --- a/backend/src/services/auth.service.js +++ b/backend/src/services/auth.service.js @@ -112,16 +112,18 @@ export const signInWithGoogle = async ({ idToken, anonymousCustomerId, deviceInf if (existing) { customer = existing } else if (anonymousCustomerId) { + // Preserve the anonymous display_name; we don't pull name from Google. customer = await upgradeCustomerIdentity(anonymousCustomerId, { google_sub: google.sub, email: google.email, - display_name: google.name, }) } else { + // No anonymous bootstrap → display_name is null; frontend routes to + // the set-display-name screen. customer = await createCustomerWithIdentity({ google_sub: google.sub, email: google.email, - display_name: google.name, + display_name: null, }) } diff --git a/backend/src/services/social-identity.service.js b/backend/src/services/social-identity.service.js index defbb0f..c317cd0 100644 --- a/backend/src/services/social-identity.service.js +++ b/backend/src/services/social-identity.service.js @@ -11,7 +11,8 @@ const googleClient = new OAuth2Client() /** * Verify a Google ID token against Google's JWKS. - * Throws on invalid; returns { sub, email, email_verified, name } on success. + * Throws on invalid; returns { sub, email, email_verified } on success. + * Intentionally omits the user's name — call sign is set in-app. */ export const verifyGoogleIdToken = async (idToken) => { const audience = getGoogleClientIds() @@ -30,7 +31,6 @@ export const verifyGoogleIdToken = async (idToken) => { sub: payload.sub, email: payload.email, email_verified: payload.email_verified === true, - name: payload.name, } } catch (err) { throw Object.assign(new Error(err.message || 'Invalid Google token'), {