fix(auth): do not blindly default to API key auth (#3235)

Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
This commit is contained in:
Pascal Birchler 2025-07-09 00:10:36 +02:00 committed by GitHub
parent 0506b40a39
commit c8cf954e6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 69 additions and 24 deletions

View File

@ -17,8 +17,8 @@ import { start_sandbox } from './utils/sandbox.js';
import { import {
LoadedSettings, LoadedSettings,
loadSettings, loadSettings,
SettingScope,
USER_SETTINGS_PATH, USER_SETTINGS_PATH,
SettingScope,
} from './config/settings.js'; } from './config/settings.js';
import { themeManager } from './ui/themes/theme-manager.js'; import { themeManager } from './ui/themes/theme-manager.js';
import { getStartupWarnings } from './utils/startupWarnings.js'; import { getStartupWarnings } from './utils/startupWarnings.js';
@ -111,15 +111,9 @@ export async function main() {
process.exit(0); process.exit(0);
} }
// Set a default auth type if one isn't set for a couple of known cases. // Set a default auth type if one isn't set.
if (!settings.merged.selectedAuthType) { if (!settings.merged.selectedAuthType) {
if (process.env.GEMINI_API_KEY) { if (process.env.CLOUD_SHELL === 'true') {
settings.setValue(
SettingScope.User,
'selectedAuthType',
AuthType.USE_GEMINI,
);
} else if (process.env.CLOUD_SHELL === 'true') {
settings.setValue( settings.setValue(
SettingScope.User, SettingScope.User,
'selectedAuthType', 'selectedAuthType',

View File

@ -5,7 +5,7 @@
*/ */
import { render } from 'ink-testing-library'; import { render } from 'ink-testing-library';
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { AuthDialog } from './AuthDialog.js'; import { AuthDialog } from './AuthDialog.js';
import { LoadedSettings, SettingScope } from '../../config/settings.js'; import { LoadedSettings, SettingScope } from '../../config/settings.js';
import { AuthType } from '@google/gemini-cli-core'; import { AuthType } from '@google/gemini-cli-core';
@ -13,7 +13,21 @@ import { AuthType } from '@google/gemini-cli-core';
describe('AuthDialog', () => { describe('AuthDialog', () => {
const wait = (ms = 50) => new Promise((resolve) => setTimeout(resolve, ms)); const wait = (ms = 50) => new Promise((resolve) => setTimeout(resolve, ms));
let originalEnv: NodeJS.ProcessEnv;
beforeEach(() => {
originalEnv = { ...process.env };
process.env.GEMINI_API_KEY = '';
vi.clearAllMocks();
});
afterEach(() => {
process.env = originalEnv;
});
it('should show an error if the initial auth type is invalid', () => { it('should show an error if the initial auth type is invalid', () => {
process.env.GEMINI_API_KEY = '';
const settings: LoadedSettings = new LoadedSettings( const settings: LoadedSettings = new LoadedSettings(
{ {
settings: { settings: {
@ -41,6 +55,30 @@ describe('AuthDialog', () => {
); );
}); });
it('should detect GEMINI_API_KEY environment variable', () => {
process.env.GEMINI_API_KEY = 'foobar';
const settings: LoadedSettings = new LoadedSettings(
{
settings: {
selectedAuthType: undefined,
},
path: '',
},
{
settings: {},
path: '',
},
[],
);
const { lastFrame } = render(
<AuthDialog onSelect={() => {}} settings={settings} />,
);
expect(lastFrame()).toContain('Existing API key detected (GEMINI_API_KEY)');
});
it('should prevent exiting when no auth method is selected and show error message', async () => { it('should prevent exiting when no auth method is selected and show error message', async () => {
const onSelect = vi.fn(); const onSelect = vi.fn();
const settings: LoadedSettings = new LoadedSettings( const settings: LoadedSettings = new LoadedSettings(

View File

@ -24,7 +24,11 @@ export function AuthDialog({
initialErrorMessage, initialErrorMessage,
}: AuthDialogProps): React.JSX.Element { }: AuthDialogProps): React.JSX.Element {
const [errorMessage, setErrorMessage] = useState<string | null>( const [errorMessage, setErrorMessage] = useState<string | null>(
initialErrorMessage || null, initialErrorMessage
? initialErrorMessage
: process.env.GEMINI_API_KEY
? 'Existing API key detected (GEMINI_API_KEY). Select "Gemini API Key" option to use it.'
: null,
); );
const items = [ const items = [
{ {
@ -46,13 +50,17 @@ export function AuthDialog({
{ label: 'Vertex AI', value: AuthType.USE_VERTEX_AI }, { label: 'Vertex AI', value: AuthType.USE_VERTEX_AI },
]; ];
let initialAuthIndex = items.findIndex( const initialAuthIndex = items.findIndex((item) => {
(item) => item.value === settings.merged.selectedAuthType, if (settings.merged.selectedAuthType) {
); return item.value === settings.merged.selectedAuthType;
}
if (initialAuthIndex === -1) { if (process.env.GEMINI_API_KEY) {
initialAuthIndex = 0; return item.value === AuthType.USE_GEMINI;
} }
return item.value === AuthType.LOGIN_WITH_GOOGLE;
});
const handleAuthSelect = (authMethod: AuthType) => { const handleAuthSelect = (authMethod: AuthType) => {
const error = validateAuthMethod(authMethod); const error = validateAuthMethod(authMethod);
@ -90,13 +98,18 @@ export function AuthDialog({
padding={1} padding={1}
width="100%" width="100%"
> >
<Text bold>Select Auth Method</Text> <Text bold>Get started</Text>
<RadioButtonSelect <Box marginTop={1}>
items={items} <Text>How would you like to authenticate for this project?</Text>
initialIndex={initialAuthIndex} </Box>
onSelect={handleAuthSelect} <Box marginTop={1}>
isFocused={true} <RadioButtonSelect
/> items={items}
initialIndex={initialAuthIndex}
onSelect={handleAuthSelect}
isFocused={true}
/>
</Box>
{errorMessage && ( {errorMessage && (
<Box marginTop={1}> <Box marginTop={1}>
<Text color={Colors.AccentRed}>{errorMessage}</Text> <Text color={Colors.AccentRed}>{errorMessage}</Text>