diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index d5420a3a..01644e90 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -305,7 +305,7 @@ export function setupIpcHandlers() { return runsCore.listRuns(args.cursor); }, 'oauth:connect': async (_event, args) => { - return await connectProvider(args.provider, args.clientId); + return await connectProvider(args.provider); }, 'oauth:disconnect': async (_event, args) => { return await disconnectProvider(args.provider); diff --git a/apps/x/apps/main/src/oauth-handler.ts b/apps/x/apps/main/src/oauth-handler.ts index a00d6e4f..3e694daa 100644 --- a/apps/x/apps/main/src/oauth-handler.ts +++ b/apps/x/apps/main/src/oauth-handler.ts @@ -3,12 +3,6 @@ import { createAuthServer } from './auth-server.js'; import * as oauthClient from '@x/core/dist/auth/oauth-client.js'; import type { Configuration } from '@x/core/dist/auth/oauth-client.js'; import { getProviderConfig, getAvailableProviders } from '@x/core/dist/auth/providers.js'; -import { - clearProviderClientIdOverride, - getProviderClientIdOverride, - hasProviderClientIdOverride, - setProviderClientIdOverride, -} from '@x/core/dist/auth/provider-client-id.js'; import container from '@x/core/dist/di/container.js'; import { IOAuthRepo } from '@x/core/dist/auth/repo.js'; import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js'; @@ -45,25 +39,14 @@ function getClientRegistrationRepo(): IClientRegistrationRepo { */ async function getProviderConfiguration(provider: string): Promise { const config = getProviderConfig(provider); - const resolveClientId = (): string => { - const override = getProviderClientIdOverride(provider); - if (override) { - return override; - } - if (config.client.mode === 'static' && config.client.clientId) { - return config.client.clientId; - } - throw new Error(`${provider} client ID not configured. Please provide a client ID.`); - }; if (config.discovery.mode === 'issuer') { if (config.client.mode === 'static') { // Discover endpoints, use static client ID console.log(`[OAuth] ${provider}: Discovery from issuer with static client ID`); - const clientId = resolveClientId(); return await oauthClient.discoverConfiguration( config.discovery.issuer, - clientId + config.client.clientId ); } else { // DCR mode - check for existing registration or register new @@ -100,11 +83,10 @@ async function getProviderConfiguration(provider: string): Promise { +export async function connectProvider(provider: string): Promise<{ success: boolean; error?: string }> { try { console.log(`[OAuth] Starting connection flow for ${provider}...`); const oauthRepo = getOAuthRepo(); const providerConfig = getProviderConfig(provider); - if (provider === 'google') { - const trimmedClientId = clientId?.trim(); - if (!trimmedClientId) { - return { success: false, error: 'Google client ID is required to connect.' }; - } - setProviderClientIdOverride(provider, trimmedClientId); - } - // Get or create OAuth configuration const config = await getProviderConfiguration(provider); @@ -235,9 +209,6 @@ export async function disconnectProvider(provider: string): Promise<{ success: b try { const oauthRepo = getOAuthRepo(); await oauthRepo.clearTokens(provider); - if (provider === 'google') { - clearProviderClientIdOverride(provider); - } return { success: true }; } catch (error) { console.error('OAuth disconnect failed:', error); @@ -251,9 +222,6 @@ export async function disconnectProvider(provider: string): Promise<{ success: b export async function isConnected(provider: string): Promise<{ isConnected: boolean }> { try { const oauthRepo = getOAuthRepo(); - if (provider === 'google' && !hasProviderClientIdOverride(provider)) { - return { isConnected: false }; - } const connected = await oauthRepo.isConnected(provider); return { isConnected: connected }; } catch (error) { @@ -310,10 +278,7 @@ export async function getConnectedProviders(): Promise<{ providers: string[] }> try { const oauthRepo = getOAuthRepo(); const providers = await oauthRepo.getConnectedProviders(); - const filteredProviders = providers.filter((provider) => - provider === 'google' ? hasProviderClientIdOverride(provider) : true - ); - return { providers: filteredProviders }; + return { providers }; } catch (error) { console.error('Get connected providers failed:', error); return { providers: [] }; diff --git a/apps/x/apps/renderer/src/components/connectors-popover.tsx b/apps/x/apps/renderer/src/components/connectors-popover.tsx index b252f281..ced6064f 100644 --- a/apps/x/apps/renderer/src/components/connectors-popover.tsx +++ b/apps/x/apps/renderer/src/components/connectors-popover.tsx @@ -17,8 +17,6 @@ import { import { Button } from "@/components/ui/button" import { Switch } from "@/components/ui/switch" import { Separator } from "@/components/ui/separator" -import { GoogleClientIdModal } from "@/components/google-client-id-modal" -import { getGoogleClientId, setGoogleClientId, clearGoogleClientId } from "@/lib/google-client-id-store" import { toast } from "sonner" interface ProviderState { @@ -37,7 +35,6 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps) const [providers, setProviders] = useState([]) const [providersLoading, setProvidersLoading] = useState(true) const [providerStates, setProviderStates] = useState>({}) - const [googleClientIdOpen, setGoogleClientIdOpen] = useState(false) // Granola state const [granolaEnabled, setGranolaEnabled] = useState(false) @@ -164,14 +161,15 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps) return cleanup }, [refreshAllStatuses]) - const startConnect = useCallback(async (provider: string, clientId?: string) => { + // Connect to a provider + const handleConnect = useCallback(async (provider: string) => { setProviderStates(prev => ({ ...prev, [provider]: { ...prev[provider], isConnecting: true } })) try { - const result = await window.ipc.invoke('oauth:connect', { provider, clientId }) + const result = await window.ipc.invoke('oauth:connect', { provider }) if (result.success) { // OAuth flow started - keep isConnecting state, wait for event @@ -194,27 +192,6 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps) } }, []) - // Connect to a provider - const handleConnect = useCallback(async (provider: string) => { - if (provider === 'google') { - const existingClientId = getGoogleClientId() - if (!existingClientId) { - setGoogleClientIdOpen(true) - return - } - await startConnect(provider, existingClientId) - return - } - - await startConnect(provider) - }, [startConnect]) - - const handleGoogleClientIdSubmit = useCallback((clientId: string) => { - setGoogleClientId(clientId) - setGoogleClientIdOpen(false) - startConnect('google', clientId) - }, [startConnect]) - // Disconnect from a provider const handleDisconnect = useCallback(async (provider: string) => { setProviderStates(prev => ({ @@ -226,9 +203,6 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps) const result = await window.ipc.invoke('oauth:disconnect', { provider }) if (result.success) { - if (provider === 'google') { - clearGoogleClientId() - } const displayName = provider === 'fireflies-ai' ? 'Fireflies' : provider.charAt(0).toUpperCase() + provider.slice(1) toast.success(`Disconnected from ${displayName}`) setProviderStates(prev => ({ @@ -315,13 +289,6 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps) } return ( - <> - {tooltip ? ( @@ -406,6 +373,5 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps) - ) } diff --git a/apps/x/apps/renderer/src/components/google-client-id-modal.tsx b/apps/x/apps/renderer/src/components/google-client-id-modal.tsx deleted file mode 100644 index 5727d506..00000000 --- a/apps/x/apps/renderer/src/components/google-client-id-modal.tsx +++ /dev/null @@ -1,85 +0,0 @@ -"use client" - -import { useEffect, useState } from "react" -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" - -interface GoogleClientIdModalProps { - open: boolean - onOpenChange: (open: boolean) => void - onSubmit: (clientId: string) => void - isSubmitting?: boolean -} - -export function GoogleClientIdModal({ - open, - onOpenChange, - onSubmit, - isSubmitting = false, -}: GoogleClientIdModalProps) { - const [clientId, setClientId] = useState("") - - useEffect(() => { - if (!open) { - setClientId("") - } - }, [open]) - - const trimmedClientId = clientId.trim() - const isValid = trimmedClientId.length > 0 - - const handleSubmit = () => { - if (!isValid || isSubmitting) return - onSubmit(trimmedClientId) - } - - return ( - - - - Enter Google Client ID - - This app does not store the client ID. You will be prompted each session. - - -
- - setClientId(event.target.value)} - onKeyDown={(event) => { - if (event.key === "Enter") { - event.preventDefault() - handleSubmit() - } - }} - autoFocus - /> -
-
- - -
-
-
- ) -} diff --git a/apps/x/apps/renderer/src/components/onboarding-modal.tsx b/apps/x/apps/renderer/src/components/onboarding-modal.tsx index e33654eb..48119934 100644 --- a/apps/x/apps/renderer/src/components/onboarding-modal.tsx +++ b/apps/x/apps/renderer/src/components/onboarding-modal.tsx @@ -14,8 +14,6 @@ import { import { Button } from "@/components/ui/button" import { Switch } from "@/components/ui/switch" import { cn } from "@/lib/utils" -import { GoogleClientIdModal } from "@/components/google-client-id-modal" -import { getGoogleClientId, setGoogleClientId } from "@/lib/google-client-id-store" import { toast } from "sonner" interface ProviderState { @@ -38,7 +36,6 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { const [providers, setProviders] = useState([]) const [providersLoading, setProvidersLoading] = useState(true) const [providerStates, setProviderStates] = useState>({}) - const [googleClientIdOpen, setGoogleClientIdOpen] = useState(false) // Granola state const [granolaEnabled, setGranolaEnabled] = useState(false) @@ -162,14 +159,15 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { return cleanup }, []) - const startConnect = useCallback(async (provider: string, clientId?: string) => { + // Connect to a provider + const handleConnect = useCallback(async (provider: string) => { setProviderStates(prev => ({ ...prev, [provider]: { ...prev[provider], isConnecting: true } })) try { - const result = await window.ipc.invoke('oauth:connect', { provider, clientId }) + const result = await window.ipc.invoke('oauth:connect', { provider }) if (!result.success) { toast.error(result.error || `Failed to connect to ${provider}`) @@ -188,27 +186,6 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { } }, []) - // Connect to a provider - const handleConnect = useCallback(async (provider: string) => { - if (provider === 'google') { - const existingClientId = getGoogleClientId() - if (!existingClientId) { - setGoogleClientIdOpen(true) - return - } - await startConnect(provider, existingClientId) - return - } - - await startConnect(provider) - }, [startConnect]) - - const handleGoogleClientIdSubmit = useCallback((clientId: string) => { - setGoogleClientId(clientId) - setGoogleClientIdOpen(false) - startConnect('google', clientId) - }, [startConnect]) - const handleNext = () => { if (currentStep < 2) { setCurrentStep((prev) => (prev + 1) as Step) @@ -452,13 +429,6 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) { } return ( - <> - {}}> } - ) } diff --git a/apps/x/apps/renderer/src/hooks/useOAuth.ts b/apps/x/apps/renderer/src/hooks/useOAuth.ts index f23f5975..b2777c6c 100644 --- a/apps/x/apps/renderer/src/hooks/useOAuth.ts +++ b/apps/x/apps/renderer/src/hooks/useOAuth.ts @@ -50,10 +50,10 @@ export function useOAuth(provider: string) { return cleanup; }, [provider, checkConnection]); - const connect = useCallback(async (clientId?: string) => { + const connect = useCallback(async () => { try { setIsConnecting(true); - const result = await window.ipc.invoke('oauth:connect', { provider, clientId }); + const result = await window.ipc.invoke('oauth:connect', { provider }); if (result.success) { // OAuth flow started - keep isConnecting state, wait for event // Event listener will handle the actual completion diff --git a/apps/x/apps/renderer/src/lib/google-client-id-store.ts b/apps/x/apps/renderer/src/lib/google-client-id-store.ts deleted file mode 100644 index 78898325..00000000 --- a/apps/x/apps/renderer/src/lib/google-client-id-store.ts +++ /dev/null @@ -1,17 +0,0 @@ -let googleClientId: string | null = null; - -export function getGoogleClientId(): string | null { - return googleClientId; -} - -export function setGoogleClientId(clientId: string): void { - const trimmed = clientId.trim(); - if (!trimmed) { - return; - } - googleClientId = trimmed; -} - -export function clearGoogleClientId(): void { - googleClientId = null; -} diff --git a/apps/x/packages/core/src/auth/provider-client-id.ts b/apps/x/packages/core/src/auth/provider-client-id.ts deleted file mode 100644 index 6f8f6a90..00000000 --- a/apps/x/packages/core/src/auth/provider-client-id.ts +++ /dev/null @@ -1,23 +0,0 @@ -type ProviderClientIdOverrides = Map; - -const providerClientIdOverrides: ProviderClientIdOverrides = new Map(); - -export function setProviderClientIdOverride(provider: string, clientId: string): void { - const trimmed = clientId.trim(); - if (!trimmed) { - return; - } - providerClientIdOverrides.set(provider, trimmed); -} - -export function getProviderClientIdOverride(provider: string): string | undefined { - return providerClientIdOverrides.get(provider); -} - -export function hasProviderClientIdOverride(provider: string): boolean { - return providerClientIdOverrides.has(provider); -} - -export function clearProviderClientIdOverride(provider: string): void { - providerClientIdOverrides.delete(provider); -} diff --git a/apps/x/packages/core/src/auth/providers.ts b/apps/x/packages/core/src/auth/providers.ts index bb4bdd9f..1cf703d6 100644 --- a/apps/x/packages/core/src/auth/providers.ts +++ b/apps/x/packages/core/src/auth/providers.ts @@ -22,7 +22,7 @@ const DiscoverySchema = z.discriminatedUnion('mode', [ const ClientSchema = z.discriminatedUnion('mode', [ z.object({ mode: z.literal('static'), - clientId: z.string().min(1).optional(), + clientId: z.string().min(1), }), z.object({ mode: z.literal('dcr'), @@ -58,6 +58,7 @@ const providerConfigs: ProviderConfig = { }, client: { mode: 'static', + clientId: '797410052581-ibmmvqec0l68stv5fmgh0juqfvbg08fc.apps.googleusercontent.com', }, scopes: [ 'https://www.googleapis.com/auth/gmail.readonly', diff --git a/apps/x/packages/core/src/knowledge/fireflies-client-factory.ts b/apps/x/packages/core/src/knowledge/fireflies-client-factory.ts index d8c975c7..a56d39b5 100644 --- a/apps/x/packages/core/src/knowledge/fireflies-client-factory.ts +++ b/apps/x/packages/core/src/knowledge/fireflies-client-factory.ts @@ -144,13 +144,9 @@ export class FirefliesClientFactory { if (providerConfig.client.mode === 'static') { // Discover endpoints, use static client ID console.log(`[Fireflies] Discovery mode: issuer with static client ID`); - const clientId = providerConfig.client.clientId; - if (!clientId) { - throw new Error('Fireflies client ID not configured.'); - } this.cache.config = await oauthClient.discoverConfiguration( providerConfig.discovery.issuer, - clientId + providerConfig.client.clientId ); } else { // DCR mode - need existing registration @@ -174,14 +170,10 @@ export class FirefliesClientFactory { } console.log(`[Fireflies] Using static endpoints (no discovery)`); - const clientId = providerConfig.client.clientId; - if (!clientId) { - throw new Error('Fireflies client ID not configured.'); - } this.cache.config = oauthClient.createStaticConfiguration( providerConfig.discovery.authorizationEndpoint, providerConfig.discovery.tokenEndpoint, - clientId, + providerConfig.client.clientId, providerConfig.discovery.revocationEndpoint ); } diff --git a/apps/x/packages/core/src/knowledge/google-client-factory.ts b/apps/x/packages/core/src/knowledge/google-client-factory.ts index 9a1240fb..f32f492c 100644 --- a/apps/x/packages/core/src/knowledge/google-client-factory.ts +++ b/apps/x/packages/core/src/knowledge/google-client-factory.ts @@ -3,7 +3,6 @@ import container from '../di/container.js'; import { IOAuthRepo } from '../auth/repo.js'; import { IClientRegistrationRepo } from '../auth/client-repo.js'; import { getProviderConfig } from '../auth/providers.js'; -import { getProviderClientIdOverride } from '../auth/provider-client-id.js'; import * as oauthClient from '../auth/oauth-client.js'; import type { Configuration } from '../auth/oauth-client.js'; import { OAuthTokens } from '../auth/types.js'; @@ -18,22 +17,12 @@ export class GoogleClientFactory { config: Configuration | null; client: OAuth2Client | null; tokens: OAuthTokens | null; - clientId: string | null; } = { config: null, client: null, tokens: null, - clientId: null, }; - private static resolveClientId(): string { - const override = getProviderClientIdOverride(this.PROVIDER_NAME); - if (!override) { - throw new Error('Google client ID not provided for this session.'); - } - return override; - } - /** * Get or create OAuth2Client, reusing cached instance when possible */ @@ -47,13 +36,7 @@ export class GoogleClientFactory { } // Initialize config cache if needed - try { - await this.initializeConfigCache(); - } catch (error) { - console.error("[OAuth] Failed to initialize Google OAuth configuration:", error); - this.clearCache(); - return null; - } + await this.initializeConfigCache(); if (!this.cache.config) { return null; } @@ -112,10 +95,6 @@ export class GoogleClientFactory { return false; } - if (!getProviderClientIdOverride(this.PROVIDER_NAME)) { - return false; - } - const tokens = await oauthRepo.getTokens(this.PROVIDER_NAME); if (!tokens) { return false; @@ -137,21 +116,14 @@ export class GoogleClientFactory { this.cache.config = null; this.cache.client = null; this.cache.tokens = null; - this.cache.clientId = null; } /** * Initialize cached configuration (called once) */ private static async initializeConfigCache(): Promise { - const clientId = this.resolveClientId(); - - if (this.cache.config && this.cache.clientId === clientId) { - return; // Already initialized for this client ID - } - - if (this.cache.clientId && this.cache.clientId !== clientId) { - this.clearCache(); + if (this.cache.config) { + return; // Already initialized } console.log(`[OAuth] Initializing Google OAuth configuration...`); @@ -163,7 +135,7 @@ export class GoogleClientFactory { console.log(`[OAuth] Discovery mode: issuer with static client ID`); this.cache.config = await oauthClient.discoverConfiguration( providerConfig.discovery.issuer, - clientId + providerConfig.client.clientId ); } else { // DCR mode - need existing registration @@ -190,12 +162,11 @@ export class GoogleClientFactory { this.cache.config = oauthClient.createStaticConfiguration( providerConfig.discovery.authorizationEndpoint, providerConfig.discovery.tokenEndpoint, - clientId, + providerConfig.client.clientId, providerConfig.discovery.revocationEndpoint ); } - this.cache.clientId = clientId; console.log(`[OAuth] Google OAuth configuration initialized`); } @@ -203,7 +174,17 @@ export class GoogleClientFactory { * Create OAuth2Client from OAuthTokens */ private static createClientFromTokens(tokens: OAuthTokens): OAuth2Client { - const clientId = this.resolveClientId(); + const providerConfig = getProviderConfig(this.PROVIDER_NAME); + + // Get client ID from config + let clientId: string; + if (providerConfig.client.mode === 'static') { + clientId = providerConfig.client.clientId; + } else { + // For DCR, we'd need to look up the registered client ID + // This is a fallback - normally initializeConfigCache handles this + throw new Error('Cannot create client without static client ID'); + } // Create OAuth2Client directly (PKCE flow doesn't use client secret) const client = new OAuth2Client( diff --git a/apps/x/packages/shared/src/ipc.ts b/apps/x/packages/shared/src/ipc.ts index 28689dde..93b797a9 100644 --- a/apps/x/packages/shared/src/ipc.ts +++ b/apps/x/packages/shared/src/ipc.ts @@ -175,7 +175,6 @@ const ipcSchemas = { 'oauth:connect': { req: z.object({ provider: z.string(), - clientId: z.string().optional(), }), res: z.object({ success: z.boolean(),