From 845487adee9e75558274afc727c67995bb6554b3 Mon Sep 17 00:00:00 2001 From: Konsti Wohlwend Date: Mon, 22 Jun 2026 16:11:24 -0700 Subject: [PATCH] fix: allow Google/GitHub/Microsoft OAuth in RDE onboarding (#1640) --- .../project-onboarding-wizard.test.tsx | 28 +++++++++++++++++++ .../project-onboarding-wizard.tsx | 18 ++++++------ .../new-project/page-client-parts/shared.ts | 11 ++++++-- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.test.tsx b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.test.tsx index 145853e25..81fc04449 100644 --- a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.test.tsx +++ b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.test.tsx @@ -161,6 +161,34 @@ describe("ProjectOnboardingWizard", () => { expect(normalizedState.selected_apps).toEqual(REQUIRED_APP_IDS); }); + it("preserves OAuth sign-in methods when developmentEnvironment is true but isLocalEmulator is false (RDE)", () => { + const normalizedState = normalizeProjectOnboardingState({ + selected_config_choice: "create-new", + selected_apps: [], + selected_sign_in_methods: ["credential", "google", "github"], + selected_email_theme_id: null, + selected_payments_country: "US", + }, { developmentEnvironment: true, isLocalEmulator: false }); + + expect(normalizedState.selected_sign_in_methods).toContain("google"); + expect(normalizedState.selected_sign_in_methods).toContain("github"); + }); + + it("strips OAuth sign-in methods when isLocalEmulator is true", () => { + const normalizedState = normalizeProjectOnboardingState({ + selected_config_choice: "create-new", + selected_apps: [], + selected_sign_in_methods: ["credential", "google", "github", "microsoft"], + selected_email_theme_id: null, + selected_payments_country: "US", + }, { developmentEnvironment: true, isLocalEmulator: true }); + + expect(normalizedState.selected_sign_in_methods).toContain("credential"); + expect(normalizedState.selected_sign_in_methods).not.toContain("google"); + expect(normalizedState.selected_sign_in_methods).not.toContain("github"); + expect(normalizedState.selected_sign_in_methods).not.toContain("microsoft"); + }); + it("does not offer alpha apps during app selection", () => { const alphaAppIds = Object.entries(ALL_APPS) .filter(([, app]) => app.stage === "alpha") diff --git a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.tsx b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.tsx index 3da3a0406..f7541b633 100644 --- a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.tsx +++ b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/project-onboarding-wizard.tsx @@ -96,12 +96,13 @@ export function ProjectOnboardingWizard(props: { selectedEmailThemeId: completeConfig.emails.selectedThemeId, selectedPaymentsCountry: "US", developmentEnvironment: isDevelopmentEnvironment, + isLocalEmulator, }); if (onboardingState == null) { return defaultState; } - return normalizeProjectOnboardingState(onboardingState, { developmentEnvironment: isDevelopmentEnvironment }); - }, [completeConfig, isDevelopmentEnvironment, onboardingState, project]); + return normalizeProjectOnboardingState(onboardingState, { developmentEnvironment: isDevelopmentEnvironment, isLocalEmulator }); + }, [completeConfig, isDevelopmentEnvironment, isLocalEmulator, onboardingState, project]); const initialOnboardingState = deriveCurrentOnboardingState(status); const [saving, setSaving] = useState(false); const [selectedApps, setSelectedApps] = useState>(() => new Set(initialOnboardingState.selected_apps)); @@ -282,8 +283,9 @@ export function ProjectOnboardingWizard(props: { selectedEmailThemeId: selectedEmailThemeId ?? completeConfig.emails.selectedThemeId, selectedPaymentsCountry, developmentEnvironment: isDevelopmentEnvironment, + isLocalEmulator, }); - }, [completeConfig.emails.selectedThemeId, isDevelopmentEnvironment, selectedApps, selectedConfigChoice, selectedEmailThemeId, selectedPaymentsCountry, signInMethods]); + }, [completeConfig.emails.selectedThemeId, isDevelopmentEnvironment, isLocalEmulator, selectedApps, selectedConfigChoice, selectedEmailThemeId, selectedPaymentsCountry, signInMethods]); const persistOnboardingState = useCallback(async () => { await setOnboardingState(buildOnboardingState()); @@ -300,7 +302,7 @@ export function ProjectOnboardingWizard(props: { for (const appId of ALL_APP_IDS) { configUpdate[`apps.installed.${appId}.enabled`] = selectedApps.has(appId); } - if (isDevelopmentEnvironment) { + if (isLocalEmulator) { configUpdate["auth.oauth.providers.google"] = signInMethods.has("google") ? { type: "google", allowSignIn: true, @@ -318,7 +320,7 @@ export function ProjectOnboardingWizard(props: { } : null; } return configUpdate; - }, [completeConfig.emails.selectedThemeId, isDevelopmentEnvironment, selectedApps, selectedEmailThemeId, signInMethods]); + }, [completeConfig.emails.selectedThemeId, isLocalEmulator, selectedApps, selectedEmailThemeId, signInMethods]); const buildEnvironmentOAuthConfigUpdate = useCallback(() => { const configUpdate: EnvironmentConfigOverrideOverride = {}; @@ -347,7 +349,7 @@ export function ProjectOnboardingWizard(props: { return; } - if (!isDevelopmentEnvironment) { + if (!isLocalEmulator) { const providersUpdated = await updateConfig({ adminApp: props.project.app, configUpdate: buildEnvironmentOAuthConfigUpdate(), @@ -368,7 +370,7 @@ export function ProjectOnboardingWizard(props: { buildEnvironmentOAuthConfigUpdate, finishProjectOnboarding, isLinkExistingMode, - isDevelopmentEnvironment, + isLocalEmulator, persistOnboardingState, props.project.app, clearOnboardingState, @@ -697,7 +699,7 @@ export function ProjectOnboardingWizard(props: { } if (props.status === "auth_setup") { - const availableSignInMethods = isDevelopmentEnvironment + const availableSignInMethods = isLocalEmulator ? SIGN_IN_METHODS.filter((method) => !OAUTH_SIGN_IN_METHODS.some((oauthMethod) => oauthMethod === method.id)) : SIGN_IN_METHODS; diff --git a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/shared.ts b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/shared.ts index 3e1fdd22e..d1852b25c 100644 --- a/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/shared.ts +++ b/apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/shared.ts @@ -84,7 +84,7 @@ export function isProjectOnboardingState(value: unknown): value is ProjectOnboar export function normalizeProjectOnboardingState( value: ProjectOnboardingState, - options?: { developmentEnvironment: boolean }, + options?: { developmentEnvironment: boolean, isLocalEmulator?: boolean }, ): ProjectOnboardingState { const selectedApps = ALL_APP_IDS.filter((appId) => ( value.selected_apps.some((selectedAppId) => selectedAppId === appId) @@ -94,7 +94,11 @@ export function normalizeProjectOnboardingState( .map((method) => method.id) .filter((methodId) => value.selected_sign_in_methods.some((selectedMethodId) => selectedMethodId === methodId)); const developmentEnvironment = options?.developmentEnvironment === true; - const normalizedSignInMethods = developmentEnvironment + const isLocalEmulator = options?.isLocalEmulator === true; + // Only strip OAuth sign-in methods for the local emulator, which lacks real OAuth + // infrastructure. The RDE connects to the production backend where shared OAuth + // providers work normally. + const normalizedSignInMethods = isLocalEmulator ? selectedSignInMethods.filter((methodId) => !OAUTH_SIGN_IN_METHODS.some((oauthMethod) => oauthMethod === methodId)) : selectedSignInMethods; return { @@ -113,6 +117,7 @@ export function createProjectOnboardingState(options: { selectedEmailThemeId: string | null, selectedPaymentsCountry: OnboardingPaymentsCountry, developmentEnvironment: boolean, + isLocalEmulator?: boolean, }): ProjectOnboardingState { return normalizeProjectOnboardingState({ selected_config_choice: options.selectedConfigChoice, @@ -122,7 +127,7 @@ export function createProjectOnboardingState(options: { .filter((methodId) => options.selectedSignInMethods.has(methodId)), selected_email_theme_id: options.selectedEmailThemeId, selected_payments_country: options.selectedPaymentsCountry, - }, { developmentEnvironment: options.developmentEnvironment }); + }, { developmentEnvironment: options.developmentEnvironment, isLocalEmulator: options.isLocalEmulator }); } export function isStackAppInternals(value: unknown): value is HexclaveAppInternals {