@langchain/google 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","names":[],"sources":["../../src/clients/node.ts"],"sourcesContent":["import { GoogleAuth, type GoogleAuthOptions } from \"google-auth-library\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { GCP_API_KEY_HEADER, GOOGLE_API_KEY_HEADER } from \"../const.js\";\nimport { ApiClient } from \"./index.js\";\nimport {\n GCPCredentials,\n getGCPCredentialsAccessToken,\n normalizeGCPCredentials,\n} from \"../utils/gcp-auth.js\";\nimport { ConfigurationError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\n/**\n * Configuration parameters for the NodeApiClient.\n *\n * This interface defines the authentication options available for Node.js-based\n * API clients. Authentication can be provided via an API key, GCP service\n * account credentials, or advanced Google Auth options. The client will\n * automatically check environment variables if explicit values are not provided.\n *\n * @interface\n */\nexport interface NodeApiClientParams {\n /**\n * Google API key for authentication.\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @see https://cloud.google.com/docs/authentication/api-keys\n */\n apiKey?: string;\n\n /**\n * GCP service account credentials for authentication.\n *\n * Can be provided as either:\n * - A JSON string containing the service account key\n * - A GCPCredentials object\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\n credentials?: string | GCPCredentials;\n\n /**\n * Google Auth options configuration.\n *\n * Provides fine-grained control over authentication behavior using the\n * google-auth-library. This option enables:\n * - Custom OAuth2 scopes\n * - Application Default Credentials (ADC)\n * - Impersonation and delegation\n * - Custom credential sources\n *\n * When provided, this takes precedence over simple credential-based auth\n * but is lower priority than API key authentication.\n *\n * @see https://github.com/googleapis/google-auth-library-nodejs\n * @see https://cloud.google.com/docs/authentication/application-default-credentials\n */\n googleAuthOptions?: GoogleAuthOptions;\n}\n\n/**\n * Node.js-specific API client for Google services.\n *\n * This client provides comprehensive authentication for Google API requests in\n * Node.js environments, leveraging the full capabilities of the google-auth-library.\n * It supports three authentication methods with the following priority:\n *\n * 1. **API Key Authentication**: Simple key-based authentication suitable for\n * public APIs and development environments.\n *\n * 2. **Service Account Credentials**: OAuth2-based authentication using GCP\n * service account keys. The client automatically handles token generation\n * and caching using a web-compatible implementation.\n *\n * 3. **Google Auth Library**: Advanced authentication using google-auth-library,\n * supporting Application Default Credentials (ADC), custom scopes, and\n * various credential sources.\n *\n * The client follows this authentication priority:\n * 1. Explicit `apiKey` parameter\n * 2. `GOOGLE_API_KEY` environment variable\n * 3. Explicit `credentials` parameter\n * 4. `GOOGLE_CLOUD_CREDENTIALS` environment variable\n * 5. `googleAuthOptions` parameter\n * 6. Application Default Credentials (if googleAuthOptions is provided without explicit credentials)\n *\n * @extends ApiClient\n *\n * @example\n * ```typescript\n * // Using API key\n * const client = new NodeApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // Using service account credentials\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * project_id: \"my-project\",\n * private_key_id: \"key-id\",\n * private_key: \"-----BEGIN PRIVATE KEY-----\\n...\",\n * client_email: \"service-account@project.iam.gserviceaccount.com\",\n * // ... other required fields\n * }\n * });\n *\n * // Using Google Auth with custom scopes\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * keyFilename: '/path/to/service-account-key.json'\n * }\n * });\n *\n * // Using Application Default Credentials\n * const client = new NodeApiClient({\n * googleAuthOptions: {}\n * });\n *\n * // Using environment variables\n * // Set GOOGLE_API_KEY or GOOGLE_CLOUD_CREDENTIALS\n * const client = new NodeApiClient();\n *\n * // Make authenticated requests\n * const request = new Request('https://api.google.com/...');\n * const response = await client.fetch(request);\n * ```\n *\n * @remarks\n * - API keys are added via the `X-Goog-Api-Key` header\n * - Service account tokens are added via the `Authorization` header with Bearer scheme\n * - Google Auth headers are automatically generated and added by google-auth-library\n * - Tokens are automatically cached and refreshed as needed\n * - For web/edge environments without google-auth-library, use WebApiClient instead\n *\n * @see {@link WebApiClient} for web-compatible authentication without google-auth-library\n * @see {@link GCPCredentials} for service account credential structure\n * @see {@link GoogleAuthOptions} for advanced authentication configuration\n */\nexport class NodeApiClient extends ApiClient {\n hasApiKey(): boolean {\n return typeof this.apiKey === \"string\" && this.apiKey !== \"\";\n }\n\n async getProjectId(): Promise<string> {\n if (typeof this.credentials !== \"undefined\") {\n return this.credentials.project_id;\n } else if (typeof this.googleAuth !== \"undefined\") {\n return this.googleAuth.getProjectId();\n } else {\n return super.getProjectId();\n }\n }\n\n /**\n * The Google API key used for authentication, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @protected\n */\n protected apiKey?: string;\n\n /**\n * The normalized GCP service account credentials, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable, and is normalized\n * to ensure immutability.\n *\n * @protected\n */\n protected credentials?: GCPCredentials;\n\n /**\n * The Google Auth instance for advanced authentication, if configured.\n *\n * This instance is created when `googleAuthOptions` is provided and\n * handles Application Default Credentials, custom scopes, and other\n * advanced authentication scenarios.\n *\n * @protected\n */\n protected googleAuth?: GoogleAuth;\n\n /**\n * Creates a new NodeApiClient instance.\n *\n * The constructor initializes authentication credentials by checking:\n * 1. Explicit parameters passed to the constructor\n * 2. Environment variables (`GOOGLE_API_KEY`, `GOOGLE_CLOUD_CREDENTIALS`)\n * 3. Application Default Credentials (if googleAuthOptions is provided)\n *\n * Credentials are normalized and frozen to prevent accidental modification.\n * The GoogleAuth instance is lazily initialized only when googleAuthOptions\n * is provided.\n *\n * @param params - Configuration parameters for the client. Defaults to an empty object.\n *\n * @example\n * ```typescript\n * // Minimal configuration (uses environment variables or ADC)\n * const client = new NodeApiClient();\n *\n * // With explicit API key\n * const client = new WebApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // With service account credentials as JSON string\n * const client = new WebApiClient({\n * credentials: '{\"type\":\"service_account\",...}'\n * });\n *\n * // With service account credentials as object\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * // ... other fields\n * }\n * });\n *\n * // With Google Auth options\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * projectId: 'my-project'\n * }\n * });\n * ```\n */\n constructor(protected params: NodeApiClientParams = {}) {\n super();\n\n this.apiKey = params.apiKey ?? getEnvironmentVariable(\"GOOGLE_API_KEY\");\n this.credentials = iife(() => {\n if (params.credentials) {\n return normalizeGCPCredentials(params.credentials);\n }\n const credentialsVar = getEnvironmentVariable(\"GOOGLE_CLOUD_CREDENTIALS\");\n if (credentialsVar) {\n return normalizeGCPCredentials(credentialsVar);\n }\n return undefined;\n });\n this.googleAuth = iife(() => {\n if (params.googleAuthOptions) {\n return new GoogleAuth(params.googleAuthOptions);\n }\n if (!this.apiKey && !this.credentials) {\n return new GoogleAuth();\n }\n return undefined;\n });\n }\n\n /**\n * Executes an HTTP request with appropriate Google API authentication.\n *\n * This method adds authentication headers to the request based on the\n * configured authentication method, following this priority:\n *\n * 1. If an API key is configured, adds the `X-Goog-Api-Key` header\n * 2. If service account credentials are configured, generates an access token\n * and adds it via the `Authorization` header with Bearer scheme\n * 3. If Google Auth is configured, uses google-auth-library to generate\n * appropriate authentication headers (may include Authorization, x-goog-user-project, etc.)\n *\n * The authentication priority ensures that explicit API keys take precedence,\n * followed by service account credentials, and finally Google Auth options.\n *\n * @param request - The HTTP request to execute. Headers will be modified\n * in-place to include authentication information.\n * @returns A Promise that resolves to the HTTP response\n *\n * @throws {AuthError} If service account authentication fails (e.g., invalid\n * credentials, network error, or token generation failure)\n * @throws {Error} If Google Auth authentication fails (e.g., missing credentials,\n * invalid configuration, or ADC not available)\n *\n * @example\n * ```typescript\n * const client = new NodeApiClient({ apiKey: 'your-key' });\n * const request = new Request('https://generativelanguage.googleapis.com/v1/models');\n * const response = await client.fetch(request);\n * const data = await response.json();\n * ```\n *\n * @remarks\n * - The request object is modified in-place by adding headers\n * - Service account tokens are automatically cached and refreshed\n * - Google Auth tokens are managed by google-auth-library with automatic refresh\n * - Only one authentication method is used per request (based on priority)\n * - Google Auth may add multiple headers depending on the authentication type\n */\n async fetch(request: Request): Promise<Response> {\n if (this.apiKey) {\n request.headers.set(GOOGLE_API_KEY_HEADER, this.apiKey);\n } else if (this.credentials) {\n request.headers.set(\n GCP_API_KEY_HEADER,\n `Bearer ${getGCPCredentialsAccessToken(this.credentials)}`\n );\n } else if (this.googleAuth) {\n const authHeaders = await this.googleAuth.getRequestHeaders(request.url);\n authHeaders.forEach((value, key) => {\n if (value !== null) {\n request.headers.set(key, value);\n }\n });\n }\n return fetch(request);\n }\n}\n\n/**\n * Ensures that Google Auth options include required OAuth2 scopes.\n *\n * This utility function validates and augments GoogleAuthOptions to ensure\n * that all required OAuth2 scopes are present. It handles three scenarios:\n *\n * 1. **No auth options provided**: Creates new options with required scopes\n * 2. **Auth options without scopes**: Adds required scopes to existing options\n * 3. **Auth options with scopes**: Validates that all required scopes are present\n *\n * This is particularly useful when working with Google APIs that require specific\n * scopes for access, ensuring that authentication is properly configured before\n * making API requests.\n *\n * @param authOptions - The existing Google Auth options to validate/augment.\n * Can be undefined or an empty object.\n * @param requiredScopes - Array of OAuth2 scope URLs that must be present.\n * These are typically Google API scope URLs like\n * 'https://www.googleapis.com/auth/cloud-platform'\n * @returns The validated/augmented GoogleAuthOptions with all required scopes\n *\n * @throws {Error} If authOptions already has scopes but is missing any of the\n * required scopes. This prevents accidental scope reduction that\n * could break API access.\n *\n * @example\n * ```typescript\n * // Create new options with required scopes\n * const options = ensureAuthScopes(undefined, [\n * 'https://www.googleapis.com/auth/cloud-platform'\n * ]);\n * // Result: { scopes: ['https://www.googleapis.com/auth/cloud-platform'] }\n *\n * // Add scopes to existing options\n * const options = ensureAuthScopes(\n * { projectId: 'my-project' },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: { projectId: 'my-project', scopes: [...] }\n *\n * // Validate existing scopes\n * const options = ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/cloud-platform'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: Original options (validation passed)\n *\n * // Error case - missing required scope\n * try {\n * ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/userinfo.email'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * } catch (error) {\n * // Error: Invalid auth scopes. Scopes must include ...\n * }\n * ```\n *\n * @remarks\n * - Scopes can be provided as a string or array of strings in GoogleAuthOptions\n * - The function normalizes single-string scopes to arrays for validation\n * - Required scopes are not automatically added if existing scopes are present;\n * instead, an error is thrown to prevent unintended scope changes\n * - This function does not modify the original authOptions object\n *\n * @see https://developers.google.com/identity/protocols/oauth2/scopes\n * @see {@link GoogleAuthOptions} for the full authentication configuration interface\n */\nexport function ensureAuthScopes(\n authOptions: GoogleAuthOptions,\n requiredScopes: string[]\n): GoogleAuthOptions {\n if (!authOptions) {\n return { scopes: requiredScopes };\n } else if (!authOptions.scopes) {\n return { ...authOptions, scopes: requiredScopes };\n } else {\n const existingScopes = Array.isArray(authOptions.scopes)\n ? authOptions.scopes\n : [authOptions.scopes];\n const missingScopes = requiredScopes.filter(\n (scope) => !existingScopes.includes(scope)\n );\n if (missingScopes.length > 0) {\n throw new ConfigurationError(\n `Invalid auth scopes. Scopes must include ${requiredScopes.join(\", \")}`\n );\n }\n }\n return authOptions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA,IAAa,gBAAb,cAAmC,UAAU;CA6FrB;CA5FtB,YAAqB;EACnB,OAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;CAC5D;CAEA,MAAM,eAAgC;EACpC,IAAI,OAAO,KAAK,gBAAgB,aAC9B,OAAO,KAAK,YAAY;OACnB,IAAI,OAAO,KAAK,eAAe,aACpC,OAAO,KAAK,WAAW,aAAa;OAEpC,OAAO,MAAM,aAAa;CAE9B;;;;;;;;;CAUA;;;;;;;;;;CAWA;;;;;;;;;;CAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDA,YAAY,SAAwC,CAAC,GAAG;EACtD,MAAM;EADc,KAAA,SAAA;EAGpB,KAAK,SAAS,OAAO,UAAU,uBAAuB,gBAAgB;EACtE,KAAK,cAAc,WAAW;GAC5B,IAAI,OAAO,aACT,OAAO,wBAAwB,OAAO,WAAW;GAEnD,MAAM,iBAAiB,uBAAuB,0BAA0B;GACxE,IAAI,gBACF,OAAO,wBAAwB,cAAc;EAGjD,CAAC;EACD,KAAK,aAAa,WAAW;GAC3B,IAAI,OAAO,mBACT,OAAO,IAAI,WAAW,OAAO,iBAAiB;GAEhD,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,aACxB,OAAO,IAAI,WAAW;EAG1B,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCA,MAAM,MAAM,SAAqC;EAC/C,IAAI,KAAK,QACP,QAAQ,QAAQ,IAAI,uBAAuB,KAAK,MAAM;OACjD,IAAI,KAAK,aACd,QAAQ,QAAQ,IACd,oBACA,UAAU,6BAA6B,KAAK,WAAW,GACzD;OACK,IAAI,KAAK,YAEd,CAAA,MAD0B,KAAK,WAAW,kBAAkB,QAAQ,GAAG,EAAA,CAC3D,SAAS,OAAO,QAAQ;GAClC,IAAI,UAAU,MACZ,QAAQ,QAAQ,IAAI,KAAK,KAAK;EAElC,CAAC;EAEH,OAAO,MAAM,OAAO;CACtB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,SAAgB,iBACd,aACA,gBACmB;CACnB,IAAI,CAAC,aACH,OAAO,EAAE,QAAQ,eAAe;MAC3B,IAAI,CAAC,YAAY,QACtB,OAAO;EAAE,GAAG;EAAa,QAAQ;CAAe;MAC3C;EACL,MAAM,iBAAiB,MAAM,QAAQ,YAAY,MAAM,IACnD,YAAY,SACZ,CAAC,YAAY,MAAM;EAIvB,IAHsB,eAAe,QAClC,UAAU,CAAC,eAAe,SAAS,KAAK,CAE3B,CAAC,CAAC,SAAS,GACzB,MAAM,IAAI,mBACR,4CAA4C,eAAe,KAAK,IAAI,GACtE;CAEJ;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"node.js","names":[],"sources":["../../src/clients/node.ts"],"sourcesContent":["import { GoogleAuth, type GoogleAuthOptions } from \"google-auth-library\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport {\n GCP_API_KEY_HEADER,\n GOOGLE_API_KEY_HEADER,\n VERTEX_AI_AUTH_SCOPES,\n} from \"../const.js\";\nimport { ApiClient } from \"./index.js\";\nimport {\n GCPCredentials,\n getGCPCredentialsAccessToken,\n normalizeGCPCredentials,\n} from \"../utils/gcp-auth.js\";\nimport { ConfigurationError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\n/**\n * Configuration parameters for the NodeApiClient.\n *\n * This interface defines the authentication options available for Node.js-based\n * API clients. Authentication can be provided via an API key, GCP service\n * account credentials, or advanced Google Auth options. The client will\n * automatically check environment variables if explicit values are not provided.\n *\n * @interface\n */\nexport interface NodeApiClientParams {\n /**\n * Google API key for authentication.\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @see https://cloud.google.com/docs/authentication/api-keys\n */\n apiKey?: string;\n\n /**\n * GCP service account credentials for authentication.\n *\n * Can be provided as either:\n * - A JSON string containing the service account key\n * - A GCPCredentials object\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\n credentials?: string | GCPCredentials;\n\n /**\n * Google Auth options configuration.\n *\n * Provides fine-grained control over authentication behavior using the\n * google-auth-library. This option enables:\n * - Custom OAuth2 scopes\n * - Application Default Credentials (ADC)\n * - Impersonation and delegation\n * - Custom credential sources\n *\n * When provided, this takes precedence over simple credential-based auth\n * but is lower priority than API key authentication.\n *\n * @see https://github.com/googleapis/google-auth-library-nodejs\n * @see https://cloud.google.com/docs/authentication/application-default-credentials\n */\n googleAuthOptions?: GoogleAuthOptions;\n}\n\n/**\n * Node.js-specific API client for Google services.\n *\n * This client provides comprehensive authentication for Google API requests in\n * Node.js environments, leveraging the full capabilities of the google-auth-library.\n * It supports three authentication methods with the following priority:\n *\n * 1. **API Key Authentication**: Simple key-based authentication suitable for\n * public APIs and development environments.\n *\n * 2. **Service Account Credentials**: OAuth2-based authentication using GCP\n * service account keys. The client automatically handles token generation\n * and caching using a web-compatible implementation.\n *\n * 3. **Google Auth Library**: Advanced authentication using google-auth-library,\n * supporting Application Default Credentials (ADC), custom scopes, and\n * various credential sources.\n *\n * The client follows this authentication priority:\n * 1. Explicit `apiKey` parameter\n * 2. `GOOGLE_API_KEY` environment variable\n * 3. Explicit `credentials` parameter\n * 4. `GOOGLE_CLOUD_CREDENTIALS` environment variable\n * 5. `googleAuthOptions` parameter\n * 6. Application Default Credentials (if googleAuthOptions is provided without explicit credentials)\n *\n * @extends ApiClient\n *\n * @example\n * ```typescript\n * // Using API key\n * const client = new NodeApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // Using service account credentials\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * project_id: \"my-project\",\n * private_key_id: \"key-id\",\n * private_key: \"-----BEGIN PRIVATE KEY-----\\n...\",\n * client_email: \"service-account@project.iam.gserviceaccount.com\",\n * // ... other required fields\n * }\n * });\n *\n * // Using Google Auth with custom scopes\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * keyFilename: '/path/to/service-account-key.json'\n * }\n * });\n *\n * // Using Application Default Credentials\n * const client = new NodeApiClient({\n * googleAuthOptions: {}\n * });\n *\n * // Using environment variables\n * // Set GOOGLE_API_KEY or GOOGLE_CLOUD_CREDENTIALS\n * const client = new NodeApiClient();\n *\n * // Make authenticated requests\n * const request = new Request('https://api.google.com/...');\n * const response = await client.fetch(request);\n * ```\n *\n * @remarks\n * - API keys are added via the `X-Goog-Api-Key` header\n * - Service account tokens are added via the `Authorization` header with Bearer scheme\n * - Google Auth headers are automatically generated and added by google-auth-library\n * - Tokens are automatically cached and refreshed as needed\n * - For web/edge environments without google-auth-library, use WebApiClient instead\n *\n * @see {@link WebApiClient} for web-compatible authentication without google-auth-library\n * @see {@link GCPCredentials} for service account credential structure\n * @see {@link GoogleAuthOptions} for advanced authentication configuration\n */\nexport class NodeApiClient extends ApiClient {\n hasApiKey(): boolean {\n return typeof this.apiKey === \"string\" && this.apiKey !== \"\";\n }\n\n async getProjectId(): Promise<string> {\n if (typeof this.credentials !== \"undefined\") {\n return this.credentials.project_id;\n } else if (typeof this.googleAuth !== \"undefined\") {\n return this.googleAuth.getProjectId();\n } else {\n return super.getProjectId();\n }\n }\n\n /**\n * The Google API key used for authentication, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @protected\n */\n protected apiKey?: string;\n\n /**\n * The normalized GCP service account credentials, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable, and is normalized\n * to ensure immutability.\n *\n * @protected\n */\n protected credentials?: GCPCredentials;\n\n /**\n * The Google Auth instance for advanced authentication, if configured.\n *\n * This instance is created when `googleAuthOptions` is provided and\n * handles Application Default Credentials, custom scopes, and other\n * advanced authentication scenarios.\n *\n * @protected\n */\n protected googleAuth?: GoogleAuth;\n\n /**\n * The OAuth scopes to request when authenticating with service account\n * credentials.\n *\n * The `googleAuthOptions` path already carries its scopes into\n * google-auth-library, so those are reused when present. Service account\n * credentials otherwise target Vertex AI, whose scope is `cloud-platform`.\n *\n * @protected\n */\n protected get authScopes(): string[] {\n const configured = this.params.googleAuthOptions?.scopes;\n if (typeof configured === \"string\") {\n return [configured];\n }\n if (Array.isArray(configured) && configured.length > 0) {\n return configured;\n }\n return VERTEX_AI_AUTH_SCOPES;\n }\n\n /**\n * Creates a new NodeApiClient instance.\n *\n * The constructor initializes authentication credentials by checking:\n * 1. Explicit parameters passed to the constructor\n * 2. Environment variables (`GOOGLE_API_KEY`, `GOOGLE_CLOUD_CREDENTIALS`)\n * 3. Application Default Credentials (if googleAuthOptions is provided)\n *\n * Credentials are normalized and frozen to prevent accidental modification.\n * The GoogleAuth instance is lazily initialized only when googleAuthOptions\n * is provided.\n *\n * @param params - Configuration parameters for the client. Defaults to an empty object.\n *\n * @example\n * ```typescript\n * // Minimal configuration (uses environment variables or ADC)\n * const client = new NodeApiClient();\n *\n * // With explicit API key\n * const client = new WebApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // With service account credentials as JSON string\n * const client = new WebApiClient({\n * credentials: '{\"type\":\"service_account\",...}'\n * });\n *\n * // With service account credentials as object\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * // ... other fields\n * }\n * });\n *\n * // With Google Auth options\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * projectId: 'my-project'\n * }\n * });\n * ```\n */\n constructor(protected params: NodeApiClientParams = {}) {\n super();\n\n this.apiKey = params.apiKey ?? getEnvironmentVariable(\"GOOGLE_API_KEY\");\n this.credentials = iife(() => {\n if (params.credentials) {\n return normalizeGCPCredentials(params.credentials);\n }\n const credentialsVar = getEnvironmentVariable(\"GOOGLE_CLOUD_CREDENTIALS\");\n if (credentialsVar) {\n return normalizeGCPCredentials(credentialsVar);\n }\n return undefined;\n });\n this.googleAuth = iife(() => {\n if (params.googleAuthOptions) {\n return new GoogleAuth(params.googleAuthOptions);\n }\n if (!this.apiKey && !this.credentials) {\n return new GoogleAuth();\n }\n return undefined;\n });\n }\n\n /**\n * Executes an HTTP request with appropriate Google API authentication.\n *\n * This method adds authentication headers to the request based on the\n * configured authentication method, following this priority:\n *\n * 1. If an API key is configured, adds the `X-Goog-Api-Key` header\n * 2. If service account credentials are configured, generates an access token\n * and adds it via the `Authorization` header with Bearer scheme\n * 3. If Google Auth is configured, uses google-auth-library to generate\n * appropriate authentication headers (may include Authorization, x-goog-user-project, etc.)\n *\n * The authentication priority ensures that explicit API keys take precedence,\n * followed by service account credentials, and finally Google Auth options.\n *\n * @param request - The HTTP request to execute. Headers will be modified\n * in-place to include authentication information.\n * @returns A Promise that resolves to the HTTP response\n *\n * @throws {AuthError} If service account authentication fails (e.g., invalid\n * credentials, network error, or token generation failure)\n * @throws {Error} If Google Auth authentication fails (e.g., missing credentials,\n * invalid configuration, or ADC not available)\n *\n * @example\n * ```typescript\n * const client = new NodeApiClient({ apiKey: 'your-key' });\n * const request = new Request('https://generativelanguage.googleapis.com/v1/models');\n * const response = await client.fetch(request);\n * const data = await response.json();\n * ```\n *\n * @remarks\n * - The request object is modified in-place by adding headers\n * - Service account tokens are automatically cached and refreshed\n * - Google Auth tokens are managed by google-auth-library with automatic refresh\n * - Only one authentication method is used per request (based on priority)\n * - Google Auth may add multiple headers depending on the authentication type\n */\n async fetch(request: Request): Promise<Response> {\n if (this.apiKey) {\n request.headers.set(GOOGLE_API_KEY_HEADER, this.apiKey);\n } else if (this.credentials) {\n // The service account mints its own JWT here rather than going through\n // google-auth-library, so the scopes have to be handed to the token\n // exchange explicitly; nothing downstream can add them.\n const accessToken = await getGCPCredentialsAccessToken(\n this.credentials,\n this.authScopes\n );\n request.headers.set(GCP_API_KEY_HEADER, `Bearer ${accessToken}`);\n } else if (this.googleAuth) {\n const authHeaders = await this.googleAuth.getRequestHeaders(request.url);\n authHeaders.forEach((value, key) => {\n if (value !== null) {\n request.headers.set(key, value);\n }\n });\n }\n return fetch(request);\n }\n}\n\n/**\n * Ensures that Google Auth options include required OAuth2 scopes.\n *\n * This utility function validates and augments GoogleAuthOptions to ensure\n * that all required OAuth2 scopes are present. It handles three scenarios:\n *\n * 1. **No auth options provided**: Creates new options with required scopes\n * 2. **Auth options without scopes**: Adds required scopes to existing options\n * 3. **Auth options with scopes**: Validates that all required scopes are present\n *\n * This is particularly useful when working with Google APIs that require specific\n * scopes for access, ensuring that authentication is properly configured before\n * making API requests.\n *\n * @param authOptions - The existing Google Auth options to validate/augment.\n * Can be undefined or an empty object.\n * @param requiredScopes - Array of OAuth2 scope URLs that must be present.\n * These are typically Google API scope URLs like\n * 'https://www.googleapis.com/auth/cloud-platform'\n * @returns The validated/augmented GoogleAuthOptions with all required scopes\n *\n * @throws {Error} If authOptions already has scopes but is missing any of the\n * required scopes. This prevents accidental scope reduction that\n * could break API access.\n *\n * @example\n * ```typescript\n * // Create new options with required scopes\n * const options = ensureAuthScopes(undefined, [\n * 'https://www.googleapis.com/auth/cloud-platform'\n * ]);\n * // Result: { scopes: ['https://www.googleapis.com/auth/cloud-platform'] }\n *\n * // Add scopes to existing options\n * const options = ensureAuthScopes(\n * { projectId: 'my-project' },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: { projectId: 'my-project', scopes: [...] }\n *\n * // Validate existing scopes\n * const options = ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/cloud-platform'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: Original options (validation passed)\n *\n * // Error case - missing required scope\n * try {\n * ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/userinfo.email'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * } catch (error) {\n * // Error: Invalid auth scopes. Scopes must include ...\n * }\n * ```\n *\n * @remarks\n * - Scopes can be provided as a string or array of strings in GoogleAuthOptions\n * - The function normalizes single-string scopes to arrays for validation\n * - Required scopes are not automatically added if existing scopes are present;\n * instead, an error is thrown to prevent unintended scope changes\n * - This function does not modify the original authOptions object\n *\n * @see https://developers.google.com/identity/protocols/oauth2/scopes\n * @see {@link GoogleAuthOptions} for the full authentication configuration interface\n */\nexport function ensureAuthScopes(\n authOptions: GoogleAuthOptions,\n requiredScopes: string[]\n): GoogleAuthOptions {\n if (!authOptions) {\n return { scopes: requiredScopes };\n } else if (!authOptions.scopes) {\n return { ...authOptions, scopes: requiredScopes };\n } else {\n const existingScopes = Array.isArray(authOptions.scopes)\n ? authOptions.scopes\n : [authOptions.scopes];\n const missingScopes = requiredScopes.filter(\n (scope) => !existingScopes.includes(scope)\n );\n if (missingScopes.length > 0) {\n throw new ConfigurationError(\n `Invalid auth scopes. Scopes must include ${requiredScopes.join(\", \")}`\n );\n }\n }\n return authOptions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA,IAAa,gBAAb,cAAmC,UAAU;CAkHrB;CAjHtB,YAAqB;EACnB,OAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;CAC5D;CAEA,MAAM,eAAgC;EACpC,IAAI,OAAO,KAAK,gBAAgB,aAC9B,OAAO,KAAK,YAAY;OACnB,IAAI,OAAO,KAAK,eAAe,aACpC,OAAO,KAAK,WAAW,aAAa;OAEpC,OAAO,MAAM,aAAa;CAE9B;;;;;;;;;CAUA;;;;;;;;;;CAWA;;;;;;;;;;CAWA;;;;;;;;;;;CAYA,IAAc,aAAuB;EACnC,MAAM,aAAa,KAAK,OAAO,mBAAmB;EAClD,IAAI,OAAO,eAAe,UACxB,OAAO,CAAC,UAAU;EAEpB,IAAI,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,GACnD,OAAO;EAET,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDA,YAAY,SAAwC,CAAC,GAAG;EACtD,MAAM;EADc,KAAA,SAAA;EAGpB,KAAK,SAAS,OAAO,UAAU,uBAAuB,gBAAgB;EACtE,KAAK,cAAc,WAAW;GAC5B,IAAI,OAAO,aACT,OAAO,wBAAwB,OAAO,WAAW;GAEnD,MAAM,iBAAiB,uBAAuB,0BAA0B;GACxE,IAAI,gBACF,OAAO,wBAAwB,cAAc;EAGjD,CAAC;EACD,KAAK,aAAa,WAAW;GAC3B,IAAI,OAAO,mBACT,OAAO,IAAI,WAAW,OAAO,iBAAiB;GAEhD,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,aACxB,OAAO,IAAI,WAAW;EAG1B,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCA,MAAM,MAAM,SAAqC;EAC/C,IAAI,KAAK,QACP,QAAQ,QAAQ,IAAI,uBAAuB,KAAK,MAAM;OACjD,IAAI,KAAK,aAAa;GAI3B,MAAM,cAAc,MAAM,6BACxB,KAAK,aACL,KAAK,UACP;GACA,QAAQ,QAAQ,IAAI,oBAAoB,UAAU,aAAa;EACjE,OAAO,IAAI,KAAK,YAEd,CAAA,MAD0B,KAAK,WAAW,kBAAkB,QAAQ,GAAG,EAAA,CAC3D,SAAS,OAAO,QAAQ;GAClC,IAAI,UAAU,MACZ,QAAQ,QAAQ,IAAI,KAAK,KAAK;EAElC,CAAC;EAEH,OAAO,MAAM,OAAO;CACtB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,SAAgB,iBACd,aACA,gBACmB;CACnB,IAAI,CAAC,aACH,OAAO,EAAE,QAAQ,eAAe;MAC3B,IAAI,CAAC,YAAY,QACtB,OAAO;EAAE,GAAG;EAAa,QAAQ;CAAe;MAC3C;EACL,MAAM,iBAAiB,MAAM,QAAQ,YAAY,MAAM,IACnD,YAAY,SACZ,CAAC,YAAY,MAAM;EAIvB,IAHsB,eAAe,QAClC,UAAU,CAAC,eAAe,SAAS,KAAK,CAE3B,CAAC,CAAC,SAAS,GACzB,MAAM,IAAI,mBACR,4CAA4C,eAAe,KAAK,IAAI,GACtE;CAEJ;CACA,OAAO;AACT"}
@@ -1,3 +1,4 @@
1
+ const require_const = require("../const.cjs");
1
2
  const require_misc = require("./misc.cjs");
2
3
  const require_errors = require("./errors.cjs");
3
4
  //#region src/utils/gcp-auth.ts
@@ -78,24 +79,33 @@ async function getGCPPrivateKey(credentials) {
78
79
  * Token flow as specified by RFC 7523.
79
80
  *
80
81
  * @param credentials - The GCP service account credentials used to sign the JWT
82
+ * @param scopes - The OAuth scopes the resulting access token should carry.
83
+ * Google requires these on the assertion itself: the claim set
84
+ * for an access token request must include `scope`, "a
85
+ * space-delimited list of the permissions that the application
86
+ * requests". Omitting it makes the exchange fail with
87
+ * `Invalid OAuth scope or ID token audience provided`.
81
88
  * @returns A Promise that resolves to the signed JWT string
82
89
  *
83
90
  * @throws {Error} If the private key cannot be imported or the JWT cannot be signed
84
91
  *
85
92
  * @see https://datatracker.ietf.org/doc/html/rfc7523
93
+ * @see https://developers.google.com/identity/protocols/oauth2/service-account
86
94
  * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials
87
95
  *
88
96
  * @example
89
97
  * ```typescript
90
98
  * const credentials = normalizeGCPCredentials(credentialsJson);
91
- * const customToken = await getGCPCustomToken(credentials);
99
+ * const customToken = await getGCPCustomToken(credentials, [
100
+ * "https://www.googleapis.com/auth/cloud-platform",
101
+ * ]);
92
102
  * // Use customToken to request an access token
93
103
  * ```
94
104
  */
95
- async function getGCPCustomToken(credentials) {
105
+ async function getGCPCustomToken(credentials, scopes = require_const.VERTEX_AI_AUTH_SCOPES) {
96
106
  const { SignJWT } = await getJose();
97
107
  const privateKey = await getGCPPrivateKey(credentials);
98
- return await new SignJWT().setIssuer(credentials.client_email).setAudience(credentials.token_uri).setSubject(credentials.client_email).setProtectedHeader({ alg: "RS256" }).setIssuedAt().setExpirationTime("1h").sign(privateKey);
108
+ return await new SignJWT({ scope: scopes.join(" ") }).setIssuer(credentials.client_email).setAudience(credentials.token_uri).setSubject(credentials.client_email).setProtectedHeader({ alg: "RS256" }).setIssuedAt().setExpirationTime("1h").sign(privateKey);
99
109
  }
100
110
  /**
101
111
  * Retrieves an access token for authenticating with Google Cloud APIs.
@@ -139,10 +149,11 @@ async function getGCPCustomToken(credentials) {
139
149
  * This ensures that even if a cache entry is fresh, an expired token
140
150
  * will trigger a refresh request.
141
151
  */
142
- async function getGCPCredentialsAccessToken(credentials) {
152
+ async function getGCPCredentialsAccessToken(credentials, scopes = require_const.VERTEX_AI_AUTH_SCOPES) {
143
153
  const tokenUrl = credentials.token_uri;
144
154
  const cacheKeyUrl = new URL(tokenUrl);
145
155
  cacheKeyUrl.searchParams.set("key", credentials.private_key_id);
156
+ cacheKeyUrl.searchParams.set("scope", [...scopes].sort().join(" "));
146
157
  const cacheKey = cacheKeyUrl.toString();
147
158
  const cacheValue = cache.get(cacheKey);
148
159
  let now = Math.floor(Date.now() / 1e3);
@@ -162,7 +173,7 @@ async function getGCPCredentialsAccessToken(credentials) {
162
173
  }
163
174
  const promise = require_misc.iife(async () => {
164
175
  now = Math.floor(Date.now() / 1e3);
165
- const jwt = await getGCPCustomToken(credentials);
176
+ const jwt = await getGCPCustomToken(credentials, scopes);
166
177
  const body = new URLSearchParams();
167
178
  body.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
168
179
  body.append("assertion", jwt);
@@ -1 +1 @@
1
- {"version":3,"file":"gcp-auth.cjs","names":["iife","AuthError"],"sources":["../../src/utils/gcp-auth.ts"],"sourcesContent":["/**\n * GCP authentication utilities.\n *\n * Adapted from: https://github.com/kriasoft/web-auth-library\n * Original source: web-auth-library by Kriasoft\n * License: MIT (https://github.com/kriasoft/web-auth-library/blob/main/LICENSE)\n */\n\nimport { AuthError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\ntype JoseModule = typeof import(\"jose\");\n\nlet joseModulePromise: Promise<JoseModule> | undefined;\n\nasync function getJose(): Promise<JoseModule> {\n joseModulePromise ??= import(\"jose\");\n return joseModulePromise;\n}\n\n/**\n * Google Cloud Platform service account credentials interface.\n *\n * This interface represents the structure of a GCP service account key file,\n * which is used for authenticating with Google Cloud APIs. Service account\n * keys can be downloaded from the Google Cloud Console and contain all the\n * information needed to authenticate as a service account.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\nexport interface GCPCredentials {\n /** The type of credential, typically \"service_account\" */\n type: string;\n /** The GCP project ID associated with this service account */\n project_id: string;\n /** The unique identifier for the private key */\n private_key_id: string;\n /** The RSA private key in PEM format used for signing JWTs */\n private_key: string;\n /** The unique client ID for this service account */\n client_id: string;\n /** The email address of the service account */\n client_email: string;\n /** The OAuth2 authorization endpoint URI */\n auth_uri: string;\n /** The OAuth2 token endpoint URI where access tokens are requested */\n token_uri: string;\n /** The URL for the OAuth2 provider's x509 certificate */\n auth_provider_x509_cert_url: string;\n /** The URL for this service account's x509 certificate */\n client_x509_cert_url: string;\n}\n\ninterface CacheValue {\n /** Unix timestamp (in seconds) when this cache entry was created */\n created: number;\n /** Promise that resolves to the token and its expiration time */\n promise: Promise<{ token: string; expires: number }>;\n}\n\nconst cache = new Map<string, CacheValue>();\nconst CACHE_MAX_AGE = 60 * 60; // 1 hour\n\n/**\n * Normalizes GCP credentials to a frozen GCPCredentials object.\n *\n * This function accepts credentials in either string (JSON) or object format\n * and ensures the returned value is a frozen (immutable) GCPCredentials object.\n * Freezing the credentials prevents accidental modification and ensures\n * consistency throughout the application lifecycle.\n *\n * @param credentials - The GCP service account credentials, either as a JSON string\n * or as a GCPCredentials object\n * @returns A frozen GCPCredentials object that cannot be modified\n *\n * @example\n * ```typescript\n * // From JSON string\n * const creds = normalizeGCPCredentials('{\"type\":\"service_account\",...}');\n *\n * // From object\n * const creds = normalizeGCPCredentials({\n * type: \"service_account\",\n * project_id: \"my-project\",\n * // ... other fields\n * });\n * ```\n */\nexport function normalizeGCPCredentials(\n credentials: string | GCPCredentials\n): GCPCredentials {\n return typeof credentials === \"string\"\n ? Object.freeze(JSON.parse(credentials))\n : Object.isFrozen(credentials)\n ? credentials\n : Object.freeze(credentials);\n}\n\n/**\n * Imports and returns the private key from GCP credentials for JWT signing.\n *\n * This function extracts the RSA private key from the service account credentials\n * and imports it using the PKCS#8 format with the RS256 algorithm. The returned\n * key can be used to sign JWTs for authentication with Google Cloud services.\n *\n * @param credentials - The GCP service account credentials containing the private key\n * @returns A Promise that resolves to the imported private key suitable for JWT signing\n *\n * @throws {Error} If the private key format is invalid or cannot be imported\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const privateKey = await getGCPPrivateKey(credentials);\n * // Use privateKey for signing operations\n * ```\n */\nexport async function getGCPPrivateKey(credentials: GCPCredentials) {\n const { importPKCS8 } = await getJose();\n return importPKCS8(credentials.private_key, \"RS256\");\n}\n\n/**\n * Generates a custom JWT token for authenticating with Google Cloud services.\n *\n * This function creates a signed JWT (JSON Web Token) using the service account's\n * private key. The token includes standard claims such as issuer, audience, subject,\n * issued-at time, and expiration time. This custom token can be exchanged for an\n * access token or ID token through Google's OAuth2 token endpoint.\n *\n * The generated JWT is valid for 1 hour and follows the OAuth 2.0 JWT Bearer\n * Token flow as specified by RFC 7523.\n *\n * @param credentials - The GCP service account credentials used to sign the JWT\n * @returns A Promise that resolves to the signed JWT string\n *\n * @throws {Error} If the private key cannot be imported or the JWT cannot be signed\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const customToken = await getGCPCustomToken(credentials);\n * // Use customToken to request an access token\n * ```\n */\nexport async function getGCPCustomToken(credentials: GCPCredentials) {\n const { SignJWT } = await getJose();\n const privateKey = await getGCPPrivateKey(credentials);\n const customToken = await new SignJWT()\n .setIssuer(credentials.client_email)\n .setAudience(credentials.token_uri)\n .setSubject(credentials.client_email)\n .setProtectedHeader({ alg: \"RS256\" })\n .setIssuedAt()\n .setExpirationTime(\"1h\")\n .sign(privateKey);\n return customToken;\n}\n\n/**\n * Retrieves an access token for authenticating with Google Cloud APIs.\n *\n * - Tokens are cached based on the token URI and private key ID\n * - Cache entries are valid for up to 1 hour\n * - Expired tokens trigger automatic refresh\n * - Multiple concurrent requests for the same token are deduplicated\n *\n * The function handles both access tokens and ID tokens, automatically detecting\n * which type is returned by the token endpoint and extracting the appropriate\n * expiration time.\n *\n * @param credentials - The GCP service account credentials used to request the token\n * @returns A Promise that resolves to the access token string\n *\n * @throws {AuthError} If the token request fails (e.g., invalid credentials,\n * network error, or API error)\n *\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const accessToken = await getGCPCredentialsAccessToken(credentials);\n *\n * // Use the access token in API requests\n * const response = await fetch('https://api.google.com/...', {\n * headers: {\n * 'Authorization': `Bearer ${accessToken}`\n * }\n * });\n * ```\n *\n * @remarks\n * The caching mechanism uses a two-level validation:\n * 1. Cache entry age (must be less than CACHE_MAX_AGE)\n * 2. Token expiration time (must not be expired)\n *\n * This ensures that even if a cache entry is fresh, an expired token\n * will trigger a refresh request.\n */\nexport async function getGCPCredentialsAccessToken(\n credentials: GCPCredentials\n): Promise<string> {\n const tokenUrl = credentials.token_uri;\n\n const cacheKeyUrl = new URL(tokenUrl);\n cacheKeyUrl.searchParams.set(\"key\", credentials.private_key_id);\n const cacheKey = cacheKeyUrl.toString();\n\n // Attempt to retrieve the token from the cache\n const cacheValue = cache.get(cacheKey);\n let now = Math.floor(Date.now() / 1000);\n\n if (cacheValue) {\n if (cacheValue.created > now - CACHE_MAX_AGE) {\n let token = await cacheValue.promise;\n\n if (token.expires > now) {\n return token.token;\n } else {\n const nextValue = cache.get(cacheKey);\n\n if (nextValue && nextValue !== cacheValue) {\n token = await nextValue.promise;\n if (token.expires > now) {\n return token.token;\n } else {\n cache.delete(cacheKey);\n }\n }\n }\n } else {\n cache.delete(cacheKey);\n }\n }\n\n const promise = iife(async () => {\n now = Math.floor(Date.now() / 1000);\n\n // Request a new token from the Google Cloud API\n const jwt = await getGCPCustomToken(credentials);\n const body = new URLSearchParams();\n body.append(\"grant_type\", \"urn:ietf:params:oauth:grant-type:jwt-bearer\");\n body.append(\"assertion\", jwt);\n const res = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (!res.ok) {\n throw await AuthError.fromResponse(res);\n }\n\n const data = await res.json();\n\n if (\"id_token\" in data) {\n const { decodeJwt } = await getJose();\n const claims = decodeJwt(data.id_token);\n return { token: data.id_token, expires: claims.exp as number };\n }\n\n const lastModified = res.headers.get(\"last-modified\");\n const expires = lastModified\n ? Math.floor(new Date(lastModified).valueOf() / 1000) + data.expires_in\n : now + data.expires_in;\n\n return { expires, token: data.access_token };\n });\n\n cache.set(cacheKey, { created: now, promise });\n return await promise.then((data) => data.token);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gcp-auth.cjs","names":["VERTEX_AI_AUTH_SCOPES","iife","AuthError"],"sources":["../../src/utils/gcp-auth.ts"],"sourcesContent":["/**\n * GCP authentication utilities.\n *\n * Adapted from: https://github.com/kriasoft/web-auth-library\n * Original source: web-auth-library by Kriasoft\n * License: MIT (https://github.com/kriasoft/web-auth-library/blob/main/LICENSE)\n */\n\nimport { AuthError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\nimport { VERTEX_AI_AUTH_SCOPES } from \"../const.js\";\n\ntype JoseModule = typeof import(\"jose\");\n\nlet joseModulePromise: Promise<JoseModule> | undefined;\n\nasync function getJose(): Promise<JoseModule> {\n joseModulePromise ??= import(\"jose\");\n return joseModulePromise;\n}\n\n/**\n * Google Cloud Platform service account credentials interface.\n *\n * This interface represents the structure of a GCP service account key file,\n * which is used for authenticating with Google Cloud APIs. Service account\n * keys can be downloaded from the Google Cloud Console and contain all the\n * information needed to authenticate as a service account.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\nexport interface GCPCredentials {\n /** The type of credential, typically \"service_account\" */\n type: string;\n /** The GCP project ID associated with this service account */\n project_id: string;\n /** The unique identifier for the private key */\n private_key_id: string;\n /** The RSA private key in PEM format used for signing JWTs */\n private_key: string;\n /** The unique client ID for this service account */\n client_id: string;\n /** The email address of the service account */\n client_email: string;\n /** The OAuth2 authorization endpoint URI */\n auth_uri: string;\n /** The OAuth2 token endpoint URI where access tokens are requested */\n token_uri: string;\n /** The URL for the OAuth2 provider's x509 certificate */\n auth_provider_x509_cert_url: string;\n /** The URL for this service account's x509 certificate */\n client_x509_cert_url: string;\n}\n\ninterface CacheValue {\n /** Unix timestamp (in seconds) when this cache entry was created */\n created: number;\n /** Promise that resolves to the token and its expiration time */\n promise: Promise<{ token: string; expires: number }>;\n}\n\nconst cache = new Map<string, CacheValue>();\nconst CACHE_MAX_AGE = 60 * 60; // 1 hour\n\n/**\n * Normalizes GCP credentials to a frozen GCPCredentials object.\n *\n * This function accepts credentials in either string (JSON) or object format\n * and ensures the returned value is a frozen (immutable) GCPCredentials object.\n * Freezing the credentials prevents accidental modification and ensures\n * consistency throughout the application lifecycle.\n *\n * @param credentials - The GCP service account credentials, either as a JSON string\n * or as a GCPCredentials object\n * @returns A frozen GCPCredentials object that cannot be modified\n *\n * @example\n * ```typescript\n * // From JSON string\n * const creds = normalizeGCPCredentials('{\"type\":\"service_account\",...}');\n *\n * // From object\n * const creds = normalizeGCPCredentials({\n * type: \"service_account\",\n * project_id: \"my-project\",\n * // ... other fields\n * });\n * ```\n */\nexport function normalizeGCPCredentials(\n credentials: string | GCPCredentials\n): GCPCredentials {\n return typeof credentials === \"string\"\n ? Object.freeze(JSON.parse(credentials))\n : Object.isFrozen(credentials)\n ? credentials\n : Object.freeze(credentials);\n}\n\n/**\n * Imports and returns the private key from GCP credentials for JWT signing.\n *\n * This function extracts the RSA private key from the service account credentials\n * and imports it using the PKCS#8 format with the RS256 algorithm. The returned\n * key can be used to sign JWTs for authentication with Google Cloud services.\n *\n * @param credentials - The GCP service account credentials containing the private key\n * @returns A Promise that resolves to the imported private key suitable for JWT signing\n *\n * @throws {Error} If the private key format is invalid or cannot be imported\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const privateKey = await getGCPPrivateKey(credentials);\n * // Use privateKey for signing operations\n * ```\n */\nexport async function getGCPPrivateKey(credentials: GCPCredentials) {\n const { importPKCS8 } = await getJose();\n return importPKCS8(credentials.private_key, \"RS256\");\n}\n\n/**\n * Generates a custom JWT token for authenticating with Google Cloud services.\n *\n * This function creates a signed JWT (JSON Web Token) using the service account's\n * private key. The token includes standard claims such as issuer, audience, subject,\n * issued-at time, and expiration time. This custom token can be exchanged for an\n * access token or ID token through Google's OAuth2 token endpoint.\n *\n * The generated JWT is valid for 1 hour and follows the OAuth 2.0 JWT Bearer\n * Token flow as specified by RFC 7523.\n *\n * @param credentials - The GCP service account credentials used to sign the JWT\n * @param scopes - The OAuth scopes the resulting access token should carry.\n * Google requires these on the assertion itself: the claim set\n * for an access token request must include `scope`, \"a\n * space-delimited list of the permissions that the application\n * requests\". Omitting it makes the exchange fail with\n * `Invalid OAuth scope or ID token audience provided`.\n * @returns A Promise that resolves to the signed JWT string\n *\n * @throws {Error} If the private key cannot be imported or the JWT cannot be signed\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n * @see https://developers.google.com/identity/protocols/oauth2/service-account\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const customToken = await getGCPCustomToken(credentials, [\n * \"https://www.googleapis.com/auth/cloud-platform\",\n * ]);\n * // Use customToken to request an access token\n * ```\n */\nexport async function getGCPCustomToken(\n credentials: GCPCredentials,\n scopes: string[] = VERTEX_AI_AUTH_SCOPES\n) {\n const { SignJWT } = await getJose();\n const privateKey = await getGCPPrivateKey(credentials);\n const customToken = await new SignJWT({ scope: scopes.join(\" \") })\n .setIssuer(credentials.client_email)\n .setAudience(credentials.token_uri)\n .setSubject(credentials.client_email)\n .setProtectedHeader({ alg: \"RS256\" })\n .setIssuedAt()\n .setExpirationTime(\"1h\")\n .sign(privateKey);\n return customToken;\n}\n\n/**\n * Retrieves an access token for authenticating with Google Cloud APIs.\n *\n * - Tokens are cached based on the token URI and private key ID\n * - Cache entries are valid for up to 1 hour\n * - Expired tokens trigger automatic refresh\n * - Multiple concurrent requests for the same token are deduplicated\n *\n * The function handles both access tokens and ID tokens, automatically detecting\n * which type is returned by the token endpoint and extracting the appropriate\n * expiration time.\n *\n * @param credentials - The GCP service account credentials used to request the token\n * @returns A Promise that resolves to the access token string\n *\n * @throws {AuthError} If the token request fails (e.g., invalid credentials,\n * network error, or API error)\n *\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const accessToken = await getGCPCredentialsAccessToken(credentials);\n *\n * // Use the access token in API requests\n * const response = await fetch('https://api.google.com/...', {\n * headers: {\n * 'Authorization': `Bearer ${accessToken}`\n * }\n * });\n * ```\n *\n * @remarks\n * The caching mechanism uses a two-level validation:\n * 1. Cache entry age (must be less than CACHE_MAX_AGE)\n * 2. Token expiration time (must not be expired)\n *\n * This ensures that even if a cache entry is fresh, an expired token\n * will trigger a refresh request.\n */\nexport async function getGCPCredentialsAccessToken(\n credentials: GCPCredentials,\n scopes: string[] = VERTEX_AI_AUTH_SCOPES\n): Promise<string> {\n const tokenUrl = credentials.token_uri;\n\n const cacheKeyUrl = new URL(tokenUrl);\n cacheKeyUrl.searchParams.set(\"key\", credentials.private_key_id);\n // The scopes are baked into the token that comes back, so a token minted for\n // one scope set must not be handed to a caller that asked for another.\n cacheKeyUrl.searchParams.set(\"scope\", [...scopes].sort().join(\" \"));\n const cacheKey = cacheKeyUrl.toString();\n\n // Attempt to retrieve the token from the cache\n const cacheValue = cache.get(cacheKey);\n let now = Math.floor(Date.now() / 1000);\n\n if (cacheValue) {\n if (cacheValue.created > now - CACHE_MAX_AGE) {\n let token = await cacheValue.promise;\n\n if (token.expires > now) {\n return token.token;\n } else {\n const nextValue = cache.get(cacheKey);\n\n if (nextValue && nextValue !== cacheValue) {\n token = await nextValue.promise;\n if (token.expires > now) {\n return token.token;\n } else {\n cache.delete(cacheKey);\n }\n }\n }\n } else {\n cache.delete(cacheKey);\n }\n }\n\n const promise = iife(async () => {\n now = Math.floor(Date.now() / 1000);\n\n // Request a new token from the Google Cloud API\n const jwt = await getGCPCustomToken(credentials, scopes);\n const body = new URLSearchParams();\n body.append(\"grant_type\", \"urn:ietf:params:oauth:grant-type:jwt-bearer\");\n body.append(\"assertion\", jwt);\n const res = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (!res.ok) {\n throw await AuthError.fromResponse(res);\n }\n\n const data = await res.json();\n\n if (\"id_token\" in data) {\n const { decodeJwt } = await getJose();\n const claims = decodeJwt(data.id_token);\n return { token: data.id_token, expires: claims.exp as number };\n }\n\n const lastModified = res.headers.get(\"last-modified\");\n const expires = lastModified\n ? Math.floor(new Date(lastModified).valueOf() / 1000) + data.expires_in\n : now + data.expires_in;\n\n return { expires, token: data.access_token };\n });\n\n cache.set(cacheKey, { created: now, promise });\n return await promise.then((data) => data.token);\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { VERTEX_AI_AUTH_SCOPES } from "../const.js";
1
2
  import { iife } from "./misc.js";
2
3
  import { AuthError } from "./errors.js";
3
4
  //#region src/utils/gcp-auth.ts
@@ -78,24 +79,33 @@ async function getGCPPrivateKey(credentials) {
78
79
  * Token flow as specified by RFC 7523.
79
80
  *
80
81
  * @param credentials - The GCP service account credentials used to sign the JWT
82
+ * @param scopes - The OAuth scopes the resulting access token should carry.
83
+ * Google requires these on the assertion itself: the claim set
84
+ * for an access token request must include `scope`, "a
85
+ * space-delimited list of the permissions that the application
86
+ * requests". Omitting it makes the exchange fail with
87
+ * `Invalid OAuth scope or ID token audience provided`.
81
88
  * @returns A Promise that resolves to the signed JWT string
82
89
  *
83
90
  * @throws {Error} If the private key cannot be imported or the JWT cannot be signed
84
91
  *
85
92
  * @see https://datatracker.ietf.org/doc/html/rfc7523
93
+ * @see https://developers.google.com/identity/protocols/oauth2/service-account
86
94
  * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials
87
95
  *
88
96
  * @example
89
97
  * ```typescript
90
98
  * const credentials = normalizeGCPCredentials(credentialsJson);
91
- * const customToken = await getGCPCustomToken(credentials);
99
+ * const customToken = await getGCPCustomToken(credentials, [
100
+ * "https://www.googleapis.com/auth/cloud-platform",
101
+ * ]);
92
102
  * // Use customToken to request an access token
93
103
  * ```
94
104
  */
95
- async function getGCPCustomToken(credentials) {
105
+ async function getGCPCustomToken(credentials, scopes = VERTEX_AI_AUTH_SCOPES) {
96
106
  const { SignJWT } = await getJose();
97
107
  const privateKey = await getGCPPrivateKey(credentials);
98
- return await new SignJWT().setIssuer(credentials.client_email).setAudience(credentials.token_uri).setSubject(credentials.client_email).setProtectedHeader({ alg: "RS256" }).setIssuedAt().setExpirationTime("1h").sign(privateKey);
108
+ return await new SignJWT({ scope: scopes.join(" ") }).setIssuer(credentials.client_email).setAudience(credentials.token_uri).setSubject(credentials.client_email).setProtectedHeader({ alg: "RS256" }).setIssuedAt().setExpirationTime("1h").sign(privateKey);
99
109
  }
100
110
  /**
101
111
  * Retrieves an access token for authenticating with Google Cloud APIs.
@@ -139,10 +149,11 @@ async function getGCPCustomToken(credentials) {
139
149
  * This ensures that even if a cache entry is fresh, an expired token
140
150
  * will trigger a refresh request.
141
151
  */
142
- async function getGCPCredentialsAccessToken(credentials) {
152
+ async function getGCPCredentialsAccessToken(credentials, scopes = VERTEX_AI_AUTH_SCOPES) {
143
153
  const tokenUrl = credentials.token_uri;
144
154
  const cacheKeyUrl = new URL(tokenUrl);
145
155
  cacheKeyUrl.searchParams.set("key", credentials.private_key_id);
156
+ cacheKeyUrl.searchParams.set("scope", [...scopes].sort().join(" "));
146
157
  const cacheKey = cacheKeyUrl.toString();
147
158
  const cacheValue = cache.get(cacheKey);
148
159
  let now = Math.floor(Date.now() / 1e3);
@@ -162,7 +173,7 @@ async function getGCPCredentialsAccessToken(credentials) {
162
173
  }
163
174
  const promise = iife(async () => {
164
175
  now = Math.floor(Date.now() / 1e3);
165
- const jwt = await getGCPCustomToken(credentials);
176
+ const jwt = await getGCPCustomToken(credentials, scopes);
166
177
  const body = new URLSearchParams();
167
178
  body.append("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
168
179
  body.append("assertion", jwt);
@@ -1 +1 @@
1
- {"version":3,"file":"gcp-auth.js","names":[],"sources":["../../src/utils/gcp-auth.ts"],"sourcesContent":["/**\n * GCP authentication utilities.\n *\n * Adapted from: https://github.com/kriasoft/web-auth-library\n * Original source: web-auth-library by Kriasoft\n * License: MIT (https://github.com/kriasoft/web-auth-library/blob/main/LICENSE)\n */\n\nimport { AuthError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\ntype JoseModule = typeof import(\"jose\");\n\nlet joseModulePromise: Promise<JoseModule> | undefined;\n\nasync function getJose(): Promise<JoseModule> {\n joseModulePromise ??= import(\"jose\");\n return joseModulePromise;\n}\n\n/**\n * Google Cloud Platform service account credentials interface.\n *\n * This interface represents the structure of a GCP service account key file,\n * which is used for authenticating with Google Cloud APIs. Service account\n * keys can be downloaded from the Google Cloud Console and contain all the\n * information needed to authenticate as a service account.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\nexport interface GCPCredentials {\n /** The type of credential, typically \"service_account\" */\n type: string;\n /** The GCP project ID associated with this service account */\n project_id: string;\n /** The unique identifier for the private key */\n private_key_id: string;\n /** The RSA private key in PEM format used for signing JWTs */\n private_key: string;\n /** The unique client ID for this service account */\n client_id: string;\n /** The email address of the service account */\n client_email: string;\n /** The OAuth2 authorization endpoint URI */\n auth_uri: string;\n /** The OAuth2 token endpoint URI where access tokens are requested */\n token_uri: string;\n /** The URL for the OAuth2 provider's x509 certificate */\n auth_provider_x509_cert_url: string;\n /** The URL for this service account's x509 certificate */\n client_x509_cert_url: string;\n}\n\ninterface CacheValue {\n /** Unix timestamp (in seconds) when this cache entry was created */\n created: number;\n /** Promise that resolves to the token and its expiration time */\n promise: Promise<{ token: string; expires: number }>;\n}\n\nconst cache = new Map<string, CacheValue>();\nconst CACHE_MAX_AGE = 60 * 60; // 1 hour\n\n/**\n * Normalizes GCP credentials to a frozen GCPCredentials object.\n *\n * This function accepts credentials in either string (JSON) or object format\n * and ensures the returned value is a frozen (immutable) GCPCredentials object.\n * Freezing the credentials prevents accidental modification and ensures\n * consistency throughout the application lifecycle.\n *\n * @param credentials - The GCP service account credentials, either as a JSON string\n * or as a GCPCredentials object\n * @returns A frozen GCPCredentials object that cannot be modified\n *\n * @example\n * ```typescript\n * // From JSON string\n * const creds = normalizeGCPCredentials('{\"type\":\"service_account\",...}');\n *\n * // From object\n * const creds = normalizeGCPCredentials({\n * type: \"service_account\",\n * project_id: \"my-project\",\n * // ... other fields\n * });\n * ```\n */\nexport function normalizeGCPCredentials(\n credentials: string | GCPCredentials\n): GCPCredentials {\n return typeof credentials === \"string\"\n ? Object.freeze(JSON.parse(credentials))\n : Object.isFrozen(credentials)\n ? credentials\n : Object.freeze(credentials);\n}\n\n/**\n * Imports and returns the private key from GCP credentials for JWT signing.\n *\n * This function extracts the RSA private key from the service account credentials\n * and imports it using the PKCS#8 format with the RS256 algorithm. The returned\n * key can be used to sign JWTs for authentication with Google Cloud services.\n *\n * @param credentials - The GCP service account credentials containing the private key\n * @returns A Promise that resolves to the imported private key suitable for JWT signing\n *\n * @throws {Error} If the private key format is invalid or cannot be imported\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const privateKey = await getGCPPrivateKey(credentials);\n * // Use privateKey for signing operations\n * ```\n */\nexport async function getGCPPrivateKey(credentials: GCPCredentials) {\n const { importPKCS8 } = await getJose();\n return importPKCS8(credentials.private_key, \"RS256\");\n}\n\n/**\n * Generates a custom JWT token for authenticating with Google Cloud services.\n *\n * This function creates a signed JWT (JSON Web Token) using the service account's\n * private key. The token includes standard claims such as issuer, audience, subject,\n * issued-at time, and expiration time. This custom token can be exchanged for an\n * access token or ID token through Google's OAuth2 token endpoint.\n *\n * The generated JWT is valid for 1 hour and follows the OAuth 2.0 JWT Bearer\n * Token flow as specified by RFC 7523.\n *\n * @param credentials - The GCP service account credentials used to sign the JWT\n * @returns A Promise that resolves to the signed JWT string\n *\n * @throws {Error} If the private key cannot be imported or the JWT cannot be signed\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const customToken = await getGCPCustomToken(credentials);\n * // Use customToken to request an access token\n * ```\n */\nexport async function getGCPCustomToken(credentials: GCPCredentials) {\n const { SignJWT } = await getJose();\n const privateKey = await getGCPPrivateKey(credentials);\n const customToken = await new SignJWT()\n .setIssuer(credentials.client_email)\n .setAudience(credentials.token_uri)\n .setSubject(credentials.client_email)\n .setProtectedHeader({ alg: \"RS256\" })\n .setIssuedAt()\n .setExpirationTime(\"1h\")\n .sign(privateKey);\n return customToken;\n}\n\n/**\n * Retrieves an access token for authenticating with Google Cloud APIs.\n *\n * - Tokens are cached based on the token URI and private key ID\n * - Cache entries are valid for up to 1 hour\n * - Expired tokens trigger automatic refresh\n * - Multiple concurrent requests for the same token are deduplicated\n *\n * The function handles both access tokens and ID tokens, automatically detecting\n * which type is returned by the token endpoint and extracting the appropriate\n * expiration time.\n *\n * @param credentials - The GCP service account credentials used to request the token\n * @returns A Promise that resolves to the access token string\n *\n * @throws {AuthError} If the token request fails (e.g., invalid credentials,\n * network error, or API error)\n *\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const accessToken = await getGCPCredentialsAccessToken(credentials);\n *\n * // Use the access token in API requests\n * const response = await fetch('https://api.google.com/...', {\n * headers: {\n * 'Authorization': `Bearer ${accessToken}`\n * }\n * });\n * ```\n *\n * @remarks\n * The caching mechanism uses a two-level validation:\n * 1. Cache entry age (must be less than CACHE_MAX_AGE)\n * 2. Token expiration time (must not be expired)\n *\n * This ensures that even if a cache entry is fresh, an expired token\n * will trigger a refresh request.\n */\nexport async function getGCPCredentialsAccessToken(\n credentials: GCPCredentials\n): Promise<string> {\n const tokenUrl = credentials.token_uri;\n\n const cacheKeyUrl = new URL(tokenUrl);\n cacheKeyUrl.searchParams.set(\"key\", credentials.private_key_id);\n const cacheKey = cacheKeyUrl.toString();\n\n // Attempt to retrieve the token from the cache\n const cacheValue = cache.get(cacheKey);\n let now = Math.floor(Date.now() / 1000);\n\n if (cacheValue) {\n if (cacheValue.created > now - CACHE_MAX_AGE) {\n let token = await cacheValue.promise;\n\n if (token.expires > now) {\n return token.token;\n } else {\n const nextValue = cache.get(cacheKey);\n\n if (nextValue && nextValue !== cacheValue) {\n token = await nextValue.promise;\n if (token.expires > now) {\n return token.token;\n } else {\n cache.delete(cacheKey);\n }\n }\n }\n } else {\n cache.delete(cacheKey);\n }\n }\n\n const promise = iife(async () => {\n now = Math.floor(Date.now() / 1000);\n\n // Request a new token from the Google Cloud API\n const jwt = await getGCPCustomToken(credentials);\n const body = new URLSearchParams();\n body.append(\"grant_type\", \"urn:ietf:params:oauth:grant-type:jwt-bearer\");\n body.append(\"assertion\", jwt);\n const res = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (!res.ok) {\n throw await AuthError.fromResponse(res);\n }\n\n const data = await res.json();\n\n if (\"id_token\" in data) {\n const { decodeJwt } = await getJose();\n const claims = decodeJwt(data.id_token);\n return { token: data.id_token, expires: claims.exp as number };\n }\n\n const lastModified = res.headers.get(\"last-modified\");\n const expires = lastModified\n ? Math.floor(new Date(lastModified).valueOf() / 1000) + data.expires_in\n : now + data.expires_in;\n\n return { expires, token: data.access_token };\n });\n\n cache.set(cacheKey, { created: now, promise });\n return await promise.then((data) => data.token);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gcp-auth.js","names":[],"sources":["../../src/utils/gcp-auth.ts"],"sourcesContent":["/**\n * GCP authentication utilities.\n *\n * Adapted from: https://github.com/kriasoft/web-auth-library\n * Original source: web-auth-library by Kriasoft\n * License: MIT (https://github.com/kriasoft/web-auth-library/blob/main/LICENSE)\n */\n\nimport { AuthError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\nimport { VERTEX_AI_AUTH_SCOPES } from \"../const.js\";\n\ntype JoseModule = typeof import(\"jose\");\n\nlet joseModulePromise: Promise<JoseModule> | undefined;\n\nasync function getJose(): Promise<JoseModule> {\n joseModulePromise ??= import(\"jose\");\n return joseModulePromise;\n}\n\n/**\n * Google Cloud Platform service account credentials interface.\n *\n * This interface represents the structure of a GCP service account key file,\n * which is used for authenticating with Google Cloud APIs. Service account\n * keys can be downloaded from the Google Cloud Console and contain all the\n * information needed to authenticate as a service account.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\nexport interface GCPCredentials {\n /** The type of credential, typically \"service_account\" */\n type: string;\n /** The GCP project ID associated with this service account */\n project_id: string;\n /** The unique identifier for the private key */\n private_key_id: string;\n /** The RSA private key in PEM format used for signing JWTs */\n private_key: string;\n /** The unique client ID for this service account */\n client_id: string;\n /** The email address of the service account */\n client_email: string;\n /** The OAuth2 authorization endpoint URI */\n auth_uri: string;\n /** The OAuth2 token endpoint URI where access tokens are requested */\n token_uri: string;\n /** The URL for the OAuth2 provider's x509 certificate */\n auth_provider_x509_cert_url: string;\n /** The URL for this service account's x509 certificate */\n client_x509_cert_url: string;\n}\n\ninterface CacheValue {\n /** Unix timestamp (in seconds) when this cache entry was created */\n created: number;\n /** Promise that resolves to the token and its expiration time */\n promise: Promise<{ token: string; expires: number }>;\n}\n\nconst cache = new Map<string, CacheValue>();\nconst CACHE_MAX_AGE = 60 * 60; // 1 hour\n\n/**\n * Normalizes GCP credentials to a frozen GCPCredentials object.\n *\n * This function accepts credentials in either string (JSON) or object format\n * and ensures the returned value is a frozen (immutable) GCPCredentials object.\n * Freezing the credentials prevents accidental modification and ensures\n * consistency throughout the application lifecycle.\n *\n * @param credentials - The GCP service account credentials, either as a JSON string\n * or as a GCPCredentials object\n * @returns A frozen GCPCredentials object that cannot be modified\n *\n * @example\n * ```typescript\n * // From JSON string\n * const creds = normalizeGCPCredentials('{\"type\":\"service_account\",...}');\n *\n * // From object\n * const creds = normalizeGCPCredentials({\n * type: \"service_account\",\n * project_id: \"my-project\",\n * // ... other fields\n * });\n * ```\n */\nexport function normalizeGCPCredentials(\n credentials: string | GCPCredentials\n): GCPCredentials {\n return typeof credentials === \"string\"\n ? Object.freeze(JSON.parse(credentials))\n : Object.isFrozen(credentials)\n ? credentials\n : Object.freeze(credentials);\n}\n\n/**\n * Imports and returns the private key from GCP credentials for JWT signing.\n *\n * This function extracts the RSA private key from the service account credentials\n * and imports it using the PKCS#8 format with the RS256 algorithm. The returned\n * key can be used to sign JWTs for authentication with Google Cloud services.\n *\n * @param credentials - The GCP service account credentials containing the private key\n * @returns A Promise that resolves to the imported private key suitable for JWT signing\n *\n * @throws {Error} If the private key format is invalid or cannot be imported\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const privateKey = await getGCPPrivateKey(credentials);\n * // Use privateKey for signing operations\n * ```\n */\nexport async function getGCPPrivateKey(credentials: GCPCredentials) {\n const { importPKCS8 } = await getJose();\n return importPKCS8(credentials.private_key, \"RS256\");\n}\n\n/**\n * Generates a custom JWT token for authenticating with Google Cloud services.\n *\n * This function creates a signed JWT (JSON Web Token) using the service account's\n * private key. The token includes standard claims such as issuer, audience, subject,\n * issued-at time, and expiration time. This custom token can be exchanged for an\n * access token or ID token through Google's OAuth2 token endpoint.\n *\n * The generated JWT is valid for 1 hour and follows the OAuth 2.0 JWT Bearer\n * Token flow as specified by RFC 7523.\n *\n * @param credentials - The GCP service account credentials used to sign the JWT\n * @param scopes - The OAuth scopes the resulting access token should carry.\n * Google requires these on the assertion itself: the claim set\n * for an access token request must include `scope`, \"a\n * space-delimited list of the permissions that the application\n * requests\". Omitting it makes the exchange fail with\n * `Invalid OAuth scope or ID token audience provided`.\n * @returns A Promise that resolves to the signed JWT string\n *\n * @throws {Error} If the private key cannot be imported or the JWT cannot be signed\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n * @see https://developers.google.com/identity/protocols/oauth2/service-account\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const customToken = await getGCPCustomToken(credentials, [\n * \"https://www.googleapis.com/auth/cloud-platform\",\n * ]);\n * // Use customToken to request an access token\n * ```\n */\nexport async function getGCPCustomToken(\n credentials: GCPCredentials,\n scopes: string[] = VERTEX_AI_AUTH_SCOPES\n) {\n const { SignJWT } = await getJose();\n const privateKey = await getGCPPrivateKey(credentials);\n const customToken = await new SignJWT({ scope: scopes.join(\" \") })\n .setIssuer(credentials.client_email)\n .setAudience(credentials.token_uri)\n .setSubject(credentials.client_email)\n .setProtectedHeader({ alg: \"RS256\" })\n .setIssuedAt()\n .setExpirationTime(\"1h\")\n .sign(privateKey);\n return customToken;\n}\n\n/**\n * Retrieves an access token for authenticating with Google Cloud APIs.\n *\n * - Tokens are cached based on the token URI and private key ID\n * - Cache entries are valid for up to 1 hour\n * - Expired tokens trigger automatic refresh\n * - Multiple concurrent requests for the same token are deduplicated\n *\n * The function handles both access tokens and ID tokens, automatically detecting\n * which type is returned by the token endpoint and extracting the appropriate\n * expiration time.\n *\n * @param credentials - The GCP service account credentials used to request the token\n * @returns A Promise that resolves to the access token string\n *\n * @throws {AuthError} If the token request fails (e.g., invalid credentials,\n * network error, or API error)\n *\n * @see https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials\n * @see https://datatracker.ietf.org/doc/html/rfc7523\n *\n * @example\n * ```typescript\n * const credentials = normalizeGCPCredentials(credentialsJson);\n * const accessToken = await getGCPCredentialsAccessToken(credentials);\n *\n * // Use the access token in API requests\n * const response = await fetch('https://api.google.com/...', {\n * headers: {\n * 'Authorization': `Bearer ${accessToken}`\n * }\n * });\n * ```\n *\n * @remarks\n * The caching mechanism uses a two-level validation:\n * 1. Cache entry age (must be less than CACHE_MAX_AGE)\n * 2. Token expiration time (must not be expired)\n *\n * This ensures that even if a cache entry is fresh, an expired token\n * will trigger a refresh request.\n */\nexport async function getGCPCredentialsAccessToken(\n credentials: GCPCredentials,\n scopes: string[] = VERTEX_AI_AUTH_SCOPES\n): Promise<string> {\n const tokenUrl = credentials.token_uri;\n\n const cacheKeyUrl = new URL(tokenUrl);\n cacheKeyUrl.searchParams.set(\"key\", credentials.private_key_id);\n // The scopes are baked into the token that comes back, so a token minted for\n // one scope set must not be handed to a caller that asked for another.\n cacheKeyUrl.searchParams.set(\"scope\", [...scopes].sort().join(\" \"));\n const cacheKey = cacheKeyUrl.toString();\n\n // Attempt to retrieve the token from the cache\n const cacheValue = cache.get(cacheKey);\n let now = Math.floor(Date.now() / 1000);\n\n if (cacheValue) {\n if (cacheValue.created > now - CACHE_MAX_AGE) {\n let token = await cacheValue.promise;\n\n if (token.expires > now) {\n return token.token;\n } else {\n const nextValue = cache.get(cacheKey);\n\n if (nextValue && nextValue !== cacheValue) {\n token = await nextValue.promise;\n if (token.expires > now) {\n return token.token;\n } else {\n cache.delete(cacheKey);\n }\n }\n }\n } else {\n cache.delete(cacheKey);\n }\n }\n\n const promise = iife(async () => {\n now = Math.floor(Date.now() / 1000);\n\n // Request a new token from the Google Cloud API\n const jwt = await getGCPCustomToken(credentials, scopes);\n const body = new URLSearchParams();\n body.append(\"grant_type\", \"urn:ietf:params:oauth:grant-type:jwt-bearer\");\n body.append(\"assertion\", jwt);\n const res = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (!res.ok) {\n throw await AuthError.fromResponse(res);\n }\n\n const data = await res.json();\n\n if (\"id_token\" in data) {\n const { decodeJwt } = await getJose();\n const claims = decodeJwt(data.id_token);\n return { token: data.id_token, expires: claims.exp as number };\n }\n\n const lastModified = res.headers.get(\"last-modified\");\n const expires = lastModified\n ? Math.floor(new Date(lastModified).valueOf() / 1000) + data.expires_in\n : now + data.expires_in;\n\n return { expires, token: data.access_token };\n });\n\n cache.set(cacheKey, { created: now, promise });\n return await promise.then((data) => data.token);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/google",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Google integrations for LangChain.js",
5
5
  "author": "LangChain",
6
6
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  "jose": "^6.2.10"
20
20
  },
21
21
  "peerDependencies": {
22
- "@langchain/core": "^1.2.11"
22
+ "@langchain/core": "^1.2.13"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@tsconfig/recommended": "^1.0.10",
@@ -30,7 +30,7 @@
30
30
  "vitest": "^4.1.11",
31
31
  "zod": "^3.25.76 || ^4",
32
32
  "zod-to-json-schema": "^3.25.2",
33
- "@langchain/core": "^1.2.11",
33
+ "@langchain/core": "^1.2.13",
34
34
  "@langchain/standard-tests": "0.0.23"
35
35
  },
36
36
  "publishConfig": {