mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-14 16:10:59 -07:00
172 lines
4.8 KiB
TypeScript
172 lines
4.8 KiB
TypeScript
/**
|
|
* @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,
|
|
} 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<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) {
|
|
// 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 {
|
|
onAuthError(`Failed to login. Message: ${getErrorMessage(e)}`);
|
|
}
|
|
}
|
|
})();
|
|
}, [
|
|
settings,
|
|
config,
|
|
authState,
|
|
setAuthState,
|
|
setAuthError,
|
|
onAuthError,
|
|
reloadApiKey,
|
|
]);
|
|
|
|
return {
|
|
authState,
|
|
setAuthState,
|
|
authError,
|
|
onAuthError,
|
|
apiKeyDefaultValue,
|
|
reloadApiKey,
|
|
accountSuspensionInfo,
|
|
setAccountSuspensionInfo,
|
|
};
|
|
};
|