mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 12:50:54 -04:00
build(deps): bump google-auth-library to 11.3.1 (#2387)
This commit is contained in:
@@ -51,7 +51,7 @@
|
||||
"figures": "6.1.0",
|
||||
"fuse.js": "7.5.0",
|
||||
"get-east-asian-width": "1.7.0",
|
||||
"google-auth-library": "10.6.2",
|
||||
"google-auth-library": "11.3.1",
|
||||
"graphology": "0.26.0",
|
||||
"graphology-metrics": "2.4.2",
|
||||
"https-proxy-agent": "9.1.0",
|
||||
@@ -105,7 +105,7 @@
|
||||
},
|
||||
},
|
||||
"overrides": {
|
||||
"google-auth-library": "10.6.2",
|
||||
"google-auth-library": "11.3.1",
|
||||
"ip-address": "10.2.0",
|
||||
"lodash-es": "4.18.1",
|
||||
"node-domexception": "file:vendor/node-domexception-shim",
|
||||
@@ -819,9 +819,9 @@
|
||||
|
||||
"fuse.js": ["fuse.js@7.5.0", "", {}, "sha512-sQtrEfA+ez/3G0cCZecF70oqpCRttCexYUG4mUrtWL49ULUzUyxokt5kyqwtKzj1270RaKih+hcP3qLcumccow=="],
|
||||
|
||||
"gaxios": ["gaxios@7.1.5", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg=="],
|
||||
"gaxios": ["gaxios@8.3.2", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-7VsaTExxjbDv2ZK1YuC4jbgfN33NI/6/7zdYWUHB6mcg51v4tCBZe+xzjqPzOpVzI+C5k1Kk9OQ80WVrXAHOqw=="],
|
||||
|
||||
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||
"gcp-metadata": ["gcp-metadata@9.2.2", "", { "dependencies": { "gaxios": "^8.3.0", "google-logging-utils": "^2.0.0", "json-bigint": "^1.0.0" } }, "sha512-hlaHpCBoNCzoUjMnBZq0LbUiYqPAhXFaAgyNwQqwWU44TZIYJK3RD3o2wkNdvPs8nnUXqJjTudnodLJkLMBjow=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
@@ -839,9 +839,9 @@
|
||||
|
||||
"globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||
|
||||
"google-auth-library": ["google-auth-library@10.6.2", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw=="],
|
||||
"google-auth-library": ["google-auth-library@11.3.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^8.3.0", "gcp-metadata": "^9.0.0", "google-logging-utils": "^2.0.0", "jws": "^4.0.0" } }, "sha512-mQS0R2Ev/DKkzuWSYtDeq15uhDeaWuT2qzLYqJ41LmooxVKHmn5ut3pc6dlHY6wfHW/D7FBmOYjAtkjaD1A7qA=="],
|
||||
|
||||
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
||||
"google-logging-utils": ["google-logging-utils@2.1.0", "", {}, "sha512-1R6uiq6XhXW2ZrcGtZpGgVDsYGzSmveVOn8Pp37qe1fbThHZ34e3t3eXj5mVu6PFSVx9fvB1KNVnYgVtq/BRcA=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
|
||||
+2
-2
@@ -131,7 +131,7 @@
|
||||
"figures": "6.1.0",
|
||||
"fuse.js": "7.5.0",
|
||||
"get-east-asian-width": "1.7.0",
|
||||
"google-auth-library": "10.6.2",
|
||||
"google-auth-library": "11.3.1",
|
||||
"graphology": "0.26.0",
|
||||
"graphology-metrics": "2.4.2",
|
||||
"https-proxy-agent": "9.1.0",
|
||||
@@ -213,7 +213,7 @@
|
||||
},
|
||||
"overrides": {
|
||||
"ip-address": "10.2.0",
|
||||
"google-auth-library": "10.6.2",
|
||||
"google-auth-library": "11.3.1",
|
||||
"lodash-es": "4.18.1",
|
||||
"node-domexception": "file:vendor/node-domexception-shim"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { GoogleAuth, UserRefreshClient } from 'google-auth-library'
|
||||
|
||||
import { snapshotEnv } from '../../test/envSnapshot.js'
|
||||
import {
|
||||
acquireSharedMutationLock,
|
||||
releaseSharedMutationLock,
|
||||
} from '../../test/sharedMutationLock.js'
|
||||
import { checkGcpCredentialsValid } from '../../utils/auth.js'
|
||||
import { resolveGeminiCredential } from '../../utils/geminiAuth.js'
|
||||
import { getAnthropicClient } from './client.js'
|
||||
|
||||
const originalGetClient = GoogleAuth.prototype.getClient
|
||||
const envKeys = [
|
||||
'GOOGLE_APPLICATION_CREDENTIALS', 'GOOGLE_CLOUD_PROJECT', 'GCLOUD_PROJECT',
|
||||
'GOOGLE_CLOUD_QUOTA_PROJECT', 'GOOGLE_PROJECT_ID',
|
||||
'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_SKIP_VERTEX_AUTH',
|
||||
'ANTHROPIC_VERTEX_PROJECT_ID', 'ANTHROPIC_VERTEX_BASE_URL', 'CLOUD_ML_REGION',
|
||||
'CLAUDE_CODE_USE_GEMINI', 'GEMINI_AUTH_MODE', 'GEMINI_API_KEY', 'GOOGLE_API_KEY',
|
||||
'GEMINI_ACCESS_TOKEN', 'GEMINI_BASE_URL', 'GEMINI_MODEL',
|
||||
'CLAUDE_CODE_USE_OPENAI', 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_FOUNDRY',
|
||||
'CLAUDE_CODE_USE_GITHUB', 'CLAUDE_CODE_USE_MISTRAL', 'ANTHROPIC_CUSTOM_HEADERS',
|
||||
] as const
|
||||
|
||||
let restoreEnv: () => void
|
||||
let fixtureDir: string
|
||||
let originalMacro: PropertyDescriptor | undefined
|
||||
let authInstances: GoogleAuth[]
|
||||
let tokenRequests: URLSearchParams[]
|
||||
let apiHeaders: Headers | undefined
|
||||
let apiUrl: string | undefined
|
||||
|
||||
const apiFetch = (async (input, init) => {
|
||||
apiUrl = String(input)
|
||||
apiHeaders = new Headers(init?.headers)
|
||||
const vertex = process.env.CLAUDE_CODE_USE_VERTEX === '1'
|
||||
return new Response(JSON.stringify(vertex ? {
|
||||
id: 'msg_auth', type: 'message', role: 'assistant',
|
||||
model: 'claude-sonnet-4-6', content: [{ type: 'text', text: 'ok' }],
|
||||
stop_reason: 'end_turn', stop_sequence: null,
|
||||
usage: { input_tokens: 1, output_tokens: 1 },
|
||||
} : {
|
||||
id: 'chatcmpl_auth', model: 'gemini-2.5-flash',
|
||||
choices: [{ message: { role: 'assistant', content: 'ok' }, finish_reason: 'stop' }],
|
||||
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
|
||||
}), { headers: { 'Content-Type': 'application/json' } })
|
||||
}) as typeof fetch
|
||||
|
||||
beforeEach(async () => {
|
||||
await acquireSharedMutationLock('services/api/googleAuth.integration.test.ts')
|
||||
restoreEnv = snapshotEnv(envKeys, { clear: true })
|
||||
originalMacro = Object.getOwnPropertyDescriptor(globalThis, 'MACRO')
|
||||
;(globalThis as Record<string, unknown>).MACRO = { VERSION: 'test-version' }
|
||||
fixtureDir = mkdtempSync(join(tmpdir(), 'openclaude-google-auth-'))
|
||||
const credentialsPath = join(fixtureDir, 'adc.json')
|
||||
writeFileSync(credentialsPath, JSON.stringify({
|
||||
type: 'authorized_user',
|
||||
client_id: 'fixture-client',
|
||||
client_secret: 'fixture-secret',
|
||||
refresh_token: 'fixture-refresh',
|
||||
quota_project_id: 'fixture-quota',
|
||||
}))
|
||||
process.env.GOOGLE_APPLICATION_CREDENTIALS = credentialsPath
|
||||
process.env.GOOGLE_CLOUD_PROJECT = 'fixture-project'
|
||||
authInstances = []
|
||||
tokenRequests = []
|
||||
apiHeaders = undefined
|
||||
apiUrl = undefined
|
||||
|
||||
// Keep the production constructor, ADC file loading, getClient, and token/header
|
||||
// methods real. Replace only the HTTP transport of the resulting auth client.
|
||||
spyOn(GoogleAuth.prototype, 'getClient').mockImplementation(async function (this: GoogleAuth) {
|
||||
const client = await originalGetClient.call(this)
|
||||
expect(client).toBeInstanceOf(UserRefreshClient)
|
||||
authInstances.push(this)
|
||||
client.transporter.defaults.fetchImplementation = async (url, init) => {
|
||||
expect(String(url)).toBe('https://oauth2.googleapis.com/token')
|
||||
expect(init?.method).toBe('POST')
|
||||
tokenRequests.push(new URLSearchParams(String(init?.body)))
|
||||
return new Response(JSON.stringify({
|
||||
access_token: 'fixture-access-token', token_type: 'Bearer', expires_in: 3600,
|
||||
}), { headers: { 'Content-Type': 'application/json' } })
|
||||
}
|
||||
return client
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
mock.restore()
|
||||
restoreEnv()
|
||||
if (originalMacro) {
|
||||
Object.defineProperty(globalThis, 'MACRO', originalMacro)
|
||||
} else {
|
||||
delete (globalThis as Record<string, unknown>).MACRO
|
||||
}
|
||||
rmSync(fixtureDir, { recursive: true, force: true })
|
||||
} finally {
|
||||
releaseSharedMutationLock()
|
||||
}
|
||||
})
|
||||
|
||||
function expectRealAuthFlow(): void {
|
||||
expect(authInstances).toHaveLength(1)
|
||||
expect(tokenRequests).toHaveLength(1)
|
||||
expect(Object.fromEntries(tokenRequests[0]!)).toEqual({
|
||||
client_id: 'fixture-client',
|
||||
client_secret: 'fixture-secret',
|
||||
refresh_token: 'fixture-refresh',
|
||||
grant_type: 'refresh_token',
|
||||
})
|
||||
}
|
||||
|
||||
test('Vertex loads ADC and forwards real Google auth Headers to rawPredict', async () => {
|
||||
process.env.CLAUDE_CODE_USE_VERTEX = '1'
|
||||
process.env.CLOUD_ML_REGION = 'us-east5'
|
||||
process.env.ANTHROPIC_VERTEX_PROJECT_ID = 'fixture-target'
|
||||
const client = await getAnthropicClient({
|
||||
maxRetries: 0, model: 'claude-sonnet-4-6', fetchOverride: apiFetch,
|
||||
})
|
||||
await client.messages.create({
|
||||
model: 'claude-sonnet-4-6', max_tokens: 16,
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
})
|
||||
|
||||
expectRealAuthFlow()
|
||||
expect(apiUrl).toContain('/projects/fixture-target/locations/us-east5/')
|
||||
expect(apiUrl).toEndWith(':rawPredict')
|
||||
expect(apiHeaders?.get('authorization')).toBe('Bearer fixture-access-token')
|
||||
expect(apiHeaders?.get('x-goog-user-project')).toBe('fixture-quota')
|
||||
})
|
||||
|
||||
test('Gemini loads ADC and normalizes the real getAccessToken result for requests', async () => {
|
||||
// The OpenAI shim uses the shared fetch transport rather than fetchOverride.
|
||||
spyOn(globalThis, 'fetch').mockImplementation(apiFetch)
|
||||
process.env.CLAUDE_CODE_USE_GEMINI = '1'
|
||||
process.env.GEMINI_AUTH_MODE = 'adc'
|
||||
process.env.GEMINI_MODEL = 'gemini-2.5-flash'
|
||||
process.env.GEMINI_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/openai'
|
||||
const client = await getAnthropicClient({
|
||||
maxRetries: 0, model: 'gemini-2.5-flash', fetchOverride: apiFetch,
|
||||
})
|
||||
await client.messages.create({
|
||||
model: 'gemini-2.5-flash', max_tokens: 16,
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
})
|
||||
|
||||
expectRealAuthFlow()
|
||||
expect(apiUrl).toBe('https://generativelanguage.googleapis.com/v1beta/openai/chat/completions')
|
||||
expect(apiHeaders?.get('authorization')).toBe('Bearer fixture-access-token')
|
||||
expect(apiHeaders?.get('x-goog-user-project')).toBe('fixture-project')
|
||||
})
|
||||
|
||||
test('Vertex credential validation loads ADC and retrieves a real access token', async () => {
|
||||
await expect(checkGcpCredentialsValid()).resolves.toBe(true)
|
||||
expectRealAuthFlow()
|
||||
})
|
||||
|
||||
test('Vertex validation and Gemini ADC reject malformed credential files', async () => {
|
||||
writeFileSync(process.env.GOOGLE_APPLICATION_CREDENTIALS!, '{invalid json')
|
||||
process.env.GEMINI_AUTH_MODE = 'adc'
|
||||
await expect(checkGcpCredentialsValid()).resolves.toBe(false)
|
||||
await expect(resolveGeminiCredential()).resolves.toEqual({ kind: 'none' })
|
||||
expect(tokenRequests).toHaveLength(0)
|
||||
})
|
||||
Reference in New Issue
Block a user