fix: allow Google/GitHub/Microsoft OAuth in RDE onboarding (#1640)

This commit is contained in:
Konsti Wohlwend 2026-06-22 16:11:24 -07:00 committed by GitHub
parent a66d94bb43
commit 845487adee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 11 deletions

View File

@ -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")

View File

@ -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<Set<AppId>>(() => 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;

View File

@ -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 {