| |
| |
| |
| |
| |
|
|
| 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 async function validateAuthMethodWithSettings( |
| authType: AuthType, |
| settings: LoadedSettings, |
| ): Promise<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 (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<AuthState>( |
| initialAuthError ? AuthState.Updating : AuthState.Unauthenticated, |
| ); |
|
|
| const [authError, setAuthError] = useState<string | null>(initialAuthError); |
| const [accountSuspensionInfo, setAccountSuspensionInfo] = |
| useState<AccountSuspensionInfo | null>(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) { |
| |
| reloadApiKey(); |
| } |
| }, [authState, reloadApiKey]); |
|
|
| useEffect(() => { |
| |
| (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(); |
| if (!key) { |
| setAuthState(AuthState.AwaitingApiKeyInput); |
| return; |
| } |
| } |
|
|
| const error = await validateAuthMethodWithSettings( |
| authType, |
| settings, |
| ).catch((e: unknown) => getErrorMessage(e)); |
|
|
| if (error) { |
| onAuthError(error); |
| return; |
| } |
|
|
| const defaultAuthType = process.env['GEMINI_DEFAULT_AUTH_TYPE']; |
| if ( |
| defaultAuthType && |
| |
| !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) { |
| |
| |
| 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, |
| }; |
| }; |
|
|