From f6562a9895e64ecf812428feeee763319e0dfe98 Mon Sep 17 00:00:00 2001 From: tusharmagar Date: Mon, 13 Apr 2026 14:08:22 +0530 Subject: [PATCH] feat(oauth): enhance Rowboat sign-in process to prevent duplicate users Added billing information checks during the Rowboat OAuth connection and onboarding process to ensure user and Stripe customer existence before proceeding. This change mitigates the risk of creating duplicate users due to parallel API calls. Updated error handling for better debugging in case of failures. --- apps/x/apps/main/src/oauth-handler.ts | 12 ++++++++++++ .../components/onboarding/use-onboarding-state.ts | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/apps/x/apps/main/src/oauth-handler.ts b/apps/x/apps/main/src/oauth-handler.ts index 483f25ee..3bb9063b 100644 --- a/apps/x/apps/main/src/oauth-handler.ts +++ b/apps/x/apps/main/src/oauth-handler.ts @@ -11,6 +11,7 @@ import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gma import { triggerSync as triggerCalendarSync } from '@x/core/dist/knowledge/sync_calendar.js'; import { triggerSync as triggerFirefliesSync } from '@x/core/dist/knowledge/sync_fireflies.js'; import { emitOAuthEvent } from './ipc.js'; +import { getBillingInfo } from '@x/core/dist/billing/billing.js'; const REDIRECT_URI = 'http://localhost:8080/oauth/callback'; @@ -271,6 +272,17 @@ export async function connectProvider(provider: string, credentials?: { clientId triggerFirefliesSync(); } + // For Rowboat sign-in, ensure user + Stripe customer exist before + // notifying the renderer. Without this, parallel API calls from + // multiple renderer hooks race to create the user, causing duplicates. + if (provider === 'rowboat') { + try { + await getBillingInfo(); + } catch (meError) { + console.error('[OAuth] Failed to initialize user via /v1/me:', meError); + } + } + // Emit success event to renderer emitOAuthEvent({ provider, success: true }); } catch (error) { diff --git a/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts b/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts index a55b23fe..2f695f1a 100644 --- a/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts +++ b/apps/x/apps/renderer/src/components/onboarding/use-onboarding-state.ts @@ -535,6 +535,14 @@ export function useOnboardingState(open: boolean, onComplete: () => void) { const cleanup = window.ipc.on('oauth:didConnect', async (event) => { if (event.provider === 'rowboat' && event.success) { + // Ensure user record exists before advancing (prevents duplicate + // Stripe customers from parallel API calls in later steps) + try { + await window.ipc.invoke('billing:getInfo', null) + } catch (error) { + console.error('Failed to fetch billing info during onboarding:', error) + } + // Re-check composio flags now that the account is connected try { const [googleResult, calendarResult] = await Promise.all([