From 5fe4e02310bdeae9ede4e6fe58dc77549b94d7cb Mon Sep 17 00:00:00 2001 From: Gaurav <39389231+gsquared94@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:11:19 -0700 Subject: [PATCH] fix: GCA creds loading order (#6498) --- packages/core/src/code_assist/oauth2.test.ts | 64 ++++++++++++++++++++ packages/core/src/code_assist/oauth2.ts | 39 ++++++------ 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/packages/core/src/code_assist/oauth2.test.ts b/packages/core/src/code_assist/oauth2.test.ts index 821788d5..25718cb1 100644 --- a/packages/core/src/code_assist/oauth2.test.ts +++ b/packages/core/src/code_assist/oauth2.test.ts @@ -324,6 +324,70 @@ describe('oauth2', () => { }); }); + describe('credential loading order', () => { + it('should prioritize default cached credentials over GOOGLE_APPLICATION_CREDENTIALS', async () => { + // Setup default cached credentials + const defaultCreds = { refresh_token: 'default-cached-token' }; + const defaultCredsPath = path.join( + tempHomeDir, + '.gemini', + 'oauth_creds.json', + ); + await fs.promises.mkdir(path.dirname(defaultCredsPath), { + recursive: true, + }); + await fs.promises.writeFile( + defaultCredsPath, + JSON.stringify(defaultCreds), + ); + + // Setup credentials via environment variable + const envCreds = { refresh_token: 'env-var-token' }; + const envCredsPath = path.join(tempHomeDir, 'env_creds.json'); + await fs.promises.writeFile(envCredsPath, JSON.stringify(envCreds)); + vi.stubEnv('GOOGLE_APPLICATION_CREDENTIALS', envCredsPath); + + const mockClient = { + setCredentials: vi.fn(), + getAccessToken: vi.fn().mockResolvedValue({ token: 'test-token' }), + getTokenInfo: vi.fn().mockResolvedValue({}), + on: vi.fn(), + }; + (OAuth2Client as unknown as Mock).mockImplementation( + () => mockClient as unknown as OAuth2Client, + ); + + await getOauthClient(AuthType.LOGIN_WITH_GOOGLE, mockConfig); + + // Assert the correct credentials were used + expect(mockClient.setCredentials).toHaveBeenCalledWith(defaultCreds); + expect(mockClient.setCredentials).not.toHaveBeenCalledWith(envCreds); + }); + + it('should fall back to GOOGLE_APPLICATION_CREDENTIALS if default cache is missing', async () => { + // Setup credentials via environment variable + const envCreds = { refresh_token: 'env-var-token' }; + const envCredsPath = path.join(tempHomeDir, 'env_creds.json'); + await fs.promises.writeFile(envCredsPath, JSON.stringify(envCreds)); + vi.stubEnv('GOOGLE_APPLICATION_CREDENTIALS', envCredsPath); + + const mockClient = { + setCredentials: vi.fn(), + getAccessToken: vi.fn().mockResolvedValue({ token: 'test-token' }), + getTokenInfo: vi.fn().mockResolvedValue({}), + on: vi.fn(), + }; + (OAuth2Client as unknown as Mock).mockImplementation( + () => mockClient as unknown as OAuth2Client, + ); + + await getOauthClient(AuthType.LOGIN_WITH_GOOGLE, mockConfig); + + // Assert the correct credentials were used + expect(mockClient.setCredentials).toHaveBeenCalledWith(envCreds); + }); + }); + describe('with GCP environment variables', () => { it('should use GOOGLE_CLOUD_ACCESS_TOKEN when GOOGLE_GENAI_USE_GCA is true', async () => { vi.stubEnv('GOOGLE_GENAI_USE_GCA', 'true'); diff --git a/packages/core/src/code_assist/oauth2.ts b/packages/core/src/code_assist/oauth2.ts index a4dcf8a7..f479dd1b 100644 --- a/packages/core/src/code_assist/oauth2.ts +++ b/packages/core/src/code_assist/oauth2.ts @@ -351,27 +351,32 @@ export function getAvailablePort(): Promise { } async function loadCachedCredentials(client: OAuth2Client): Promise { - try { - const keyFile = - process.env['GOOGLE_APPLICATION_CREDENTIALS'] || - getCachedCredentialPath(); + const pathsToTry = [ + getCachedCredentialPath(), + process.env['GOOGLE_APPLICATION_CREDENTIALS'], + ].filter((p): p is string => !!p); - const creds = await fs.readFile(keyFile, 'utf-8'); - client.setCredentials(JSON.parse(creds)); + for (const keyFile of pathsToTry) { + try { + const creds = await fs.readFile(keyFile, 'utf-8'); + client.setCredentials(JSON.parse(creds)); - // This will verify locally that the credentials look good. - const { token } = await client.getAccessToken(); - if (!token) { - return false; + // This will verify locally that the credentials look good. + const { token } = await client.getAccessToken(); + if (!token) { + continue; + } + + // This will check with the server to see if it hasn't been revoked. + await client.getTokenInfo(token); + + return true; + } catch (_) { + // Ignore and try next path. } - - // This will check with the server to see if it hasn't been revoked. - await client.getTokenInfo(token); - - return true; - } catch (_) { - return false; } + + return false; } async function cacheCredentials(credentials: Credentials) {