/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { useState, useEffect, useCallback } from 'react'; import type { LoadedSettings } from '../../config/settings.js'; import { AuthType, type Config, loadApiKey, debugLogger, isAccountSuspendedError, ProjectIdRequiredError, } from '@google/gemini-cli-core'; import { getErrorMessage } from '@google/gemini-cli-core'; import { AuthState } from '../types.js'; import { validateAuthMethod } from '../../config/auth.js'; export function validateAuthMethodWithSettings( authType: AuthType, settings: LoadedSettings, ): string | null { const enforcedType = settings.merged.security.auth.enforcedType; if (enforcedType && enforcedType !== authType) { return `Authentication is enforced to be ${enforcedType}, but you are currently using ${authType}.`; } if (settings.merged.security.auth.useExternal) { return null; } // If using Gemini API key, we don't validate it here as we might need to prompt for it. if (authType === AuthType.USE_GEMINI) { return null; } return validateAuthMethod(authType); } import type { AccountSuspensionInfo } from '../contexts/UIStateContext.js'; export const useAuthCommand = ( settings: LoadedSettings, config: Config, initialAuthError: string | null = null, initialAccountSuspensionInfo: AccountSuspensionInfo | null = null, ) => { const [authState, setAuthState] = useState( initialAuthError ? AuthState.Updating : AuthState.Unauthenticated, ); const [authError, setAuthError] = useState(initialAuthError); const [accountSuspensionInfo, setAccountSuspensionInfo] = useState(initialAccountSuspensionInfo); const [apiKeyDefaultValue, setApiKeyDefaultValue] = useState< string | undefined >(undefined); const onAuthError = useCallback( (error: string | null) => { setAuthError(error); if (error) { setAuthState(AuthState.Updating); } }, [setAuthError, setAuthState], ); const reloadApiKey = useCallback(async () => { const envKey = process.env['GEMINI_API_KEY']; if (envKey !== undefined) { setApiKeyDefaultValue(envKey); return envKey; } const storedKey = (await loadApiKey()) ?? ''; setApiKeyDefaultValue(storedKey); return storedKey; }, []); useEffect(() => { if (authState === AuthState.AwaitingApiKeyInput) { // eslint-disable-next-line @typescript-eslint/no-floating-promises reloadApiKey(); } }, [authState, reloadApiKey]); useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { if (authState !== AuthState.Unauthenticated) { return; } const authType = settings.merged.security.auth.selectedType; if (!authType) { if (process.env['GEMINI_API_KEY']) { onAuthError( 'Existing API key detected (GEMINI_API_KEY). Select "Gemini API Key" option to use it.', ); } else { onAuthError('No authentication method selected.'); } return; } if (authType === AuthType.USE_GEMINI) { const key = await reloadApiKey(); // Use the unified function if (!key) { setAuthState(AuthState.AwaitingApiKeyInput); return; } } const error = validateAuthMethodWithSettings(authType, settings); if (error) { onAuthError(error); return; } const defaultAuthType = process.env['GEMINI_DEFAULT_AUTH_TYPE']; if ( defaultAuthType && // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion !Object.values(AuthType).includes(defaultAuthType as AuthType) ) { onAuthError( `Invalid value for GEMINI_DEFAULT_AUTH_TYPE: "${defaultAuthType}". ` + `Valid values are: ${Object.values(AuthType).join(', ')}.`, ); return; } try { await config.refreshAuth(authType); debugLogger.log(`Authenticated via "${authType}".`); setAuthError(null); setAuthState(AuthState.Authenticated); } catch (e) { const suspendedError = isAccountSuspendedError(e); if (suspendedError) { setAccountSuspensionInfo({ message: suspendedError.message, appealUrl: suspendedError.appealUrl, appealLinkText: suspendedError.appealLinkText, }); } else if (e instanceof ProjectIdRequiredError) { // OAuth succeeded but account setup requires project ID // Show the error message directly without "Failed to login" prefix onAuthError(getErrorMessage(e)); } else { onAuthError(`Failed to sign in. Message: ${getErrorMessage(e)}`); } } })(); }, [ settings, config, authState, setAuthState, setAuthError, onAuthError, reloadApiKey, ]); return { authState, setAuthState, authError, onAuthError, apiKeyDefaultValue, reloadApiKey, accountSuspensionInfo, setAccountSuspensionInfo, }; };