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:
parent
0506b40a39
commit
c8cf954e6e
|
@ -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',
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,14 +50,18 @@ 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) {
|
|
||||||
initialAuthIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.GEMINI_API_KEY) {
|
||||||
|
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);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -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>
|
||||||
|
<Box marginTop={1}>
|
||||||
|
<Text>How would you like to authenticate for this project?</Text>
|
||||||
|
</Box>
|
||||||
|
<Box marginTop={1}>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
items={items}
|
items={items}
|
||||||
initialIndex={initialAuthIndex}
|
initialIndex={initialAuthIndex}
|
||||||
onSelect={handleAuthSelect}
|
onSelect={handleAuthSelect}
|
||||||
isFocused={true}
|
isFocused={true}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={Colors.AccentRed}>{errorMessage}</Text>
|
<Text color={Colors.AccentRed}>{errorMessage}</Text>
|
||||||
|
|
Loading…
Reference in New Issue