2025-09-05 15:35:41 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
|
|
|
import type { LoadedSettings } from '../../config/settings.js';
|
2025-10-29 18:58:08 -07:00
|
|
|
import {
|
|
|
|
|
AuthType,
|
|
|
|
|
type Config,
|
|
|
|
|
loadApiKey,
|
|
|
|
|
debugLogger,
|
|
|
|
|
} from '@google/gemini-cli-core';
|
2025-09-05 15:35:41 -07:00
|
|
|
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;
|
|
|
|
|
}
|
2025-10-29 18:58:08 -07:00
|
|
|
// 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;
|
|
|
|
|
}
|
2025-09-05 15:35:41 -07:00
|
|
|
return validateAuthMethod(authType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const useAuthCommand = (settings: LoadedSettings, config: Config) => {
|
|
|
|
|
const [authState, setAuthState] = useState<AuthState>(
|
|
|
|
|
AuthState.Unauthenticated,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const [authError, setAuthError] = useState<string | null>(null);
|
2025-10-29 18:58:08 -07:00
|
|
|
const [apiKeyDefaultValue, setApiKeyDefaultValue] = useState<
|
|
|
|
|
string | undefined
|
|
|
|
|
>(undefined);
|
2025-09-05 15:35:41 -07:00
|
|
|
|
|
|
|
|
const onAuthError = useCallback(
|
2025-10-16 18:08:42 -04:00
|
|
|
(error: string | null) => {
|
2025-09-05 15:35:41 -07:00
|
|
|
setAuthError(error);
|
2025-10-16 18:08:42 -04:00
|
|
|
if (error) {
|
|
|
|
|
setAuthState(AuthState.Updating);
|
|
|
|
|
}
|
2025-09-05 15:35:41 -07:00
|
|
|
},
|
|
|
|
|
[setAuthError, setAuthState],
|
|
|
|
|
);
|
|
|
|
|
|
2025-10-29 18:58:08 -07:00
|
|
|
const reloadApiKey = useCallback(async () => {
|
|
|
|
|
const storedKey = (await loadApiKey()) ?? '';
|
|
|
|
|
const envKey = process.env['GEMINI_API_KEY'] ?? '';
|
|
|
|
|
const key = storedKey || envKey;
|
|
|
|
|
setApiKeyDefaultValue(key);
|
|
|
|
|
return key; // Return the key for immediate use
|
|
|
|
|
}, []);
|
|
|
|
|
|
2025-09-05 15:35:41 -07:00
|
|
|
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;
|
|
|
|
|
}
|
2025-10-29 18:58:08 -07:00
|
|
|
|
|
|
|
|
if (authType === AuthType.USE_GEMINI) {
|
|
|
|
|
const key = await reloadApiKey(); // Use the unified function
|
|
|
|
|
if (!key) {
|
|
|
|
|
setAuthState(AuthState.AwaitingApiKeyInput);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 15:35:41 -07:00
|
|
|
const error = validateAuthMethodWithSettings(authType, settings);
|
|
|
|
|
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);
|
|
|
|
|
|
2025-10-21 16:35:22 -04:00
|
|
|
debugLogger.log(`Authenticated via "${authType}".`);
|
2025-09-05 15:35:41 -07:00
|
|
|
setAuthError(null);
|
|
|
|
|
setAuthState(AuthState.Authenticated);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
onAuthError(`Failed to login. Message: ${getErrorMessage(e)}`);
|
|
|
|
|
}
|
|
|
|
|
})();
|
2025-10-29 18:58:08 -07:00
|
|
|
}, [
|
|
|
|
|
settings,
|
|
|
|
|
config,
|
|
|
|
|
authState,
|
|
|
|
|
setAuthState,
|
|
|
|
|
setAuthError,
|
|
|
|
|
onAuthError,
|
|
|
|
|
reloadApiKey,
|
|
|
|
|
]);
|
2025-09-05 15:35:41 -07:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
authState,
|
|
|
|
|
setAuthState,
|
|
|
|
|
authError,
|
|
|
|
|
onAuthError,
|
2025-10-29 18:58:08 -07:00
|
|
|
apiKeyDefaultValue,
|
|
|
|
|
reloadApiKey,
|
2025-09-05 15:35:41 -07:00
|
|
|
};
|
|
|
|
|
};
|