diff --git a/.changeset/shy-dolls-admire.md b/.changeset/shy-dolls-admire.md new file mode 100644 index 000000000000..1cc88476b222 --- /dev/null +++ b/.changeset/shy-dolls-admire.md @@ -0,0 +1,5 @@ +--- +"@langchain/google": patch +--- + +lazy-load jose in CJS auth helpers diff --git a/libs/providers/langchain-google/src/utils/gcp-auth.ts b/libs/providers/langchain-google/src/utils/gcp-auth.ts index d35533c86969..63fa8469453d 100644 --- a/libs/providers/langchain-google/src/utils/gcp-auth.ts +++ b/libs/providers/langchain-google/src/utils/gcp-auth.ts @@ -6,10 +6,18 @@ * License: MIT (https://github.com/kriasoft/web-auth-library/blob/main/LICENSE) */ -import { decodeJwt, importPKCS8, SignJWT } from "jose"; import { AuthError } from "../utils/errors.js"; import { iife } from "../utils/misc.js"; +type JoseModule = typeof import("jose"); + +let joseModulePromise: Promise | undefined; + +async function getJose(): Promise { + joseModulePromise ??= import("jose"); + return joseModulePromise; +} + /** * Google Cloud Platform service account credentials interface. * @@ -107,7 +115,8 @@ export function normalizeGCPCredentials( * // Use privateKey for signing operations * ``` */ -export function getGCPPrivateKey(credentials: GCPCredentials) { +export async function getGCPPrivateKey(credentials: GCPCredentials) { + const { importPKCS8 } = await getJose(); return importPKCS8(credentials.private_key, "RS256"); } @@ -138,6 +147,7 @@ export function getGCPPrivateKey(credentials: GCPCredentials) { * ``` */ export async function getGCPCustomToken(credentials: GCPCredentials) { + const { SignJWT } = await getJose(); const privateKey = await getGCPPrivateKey(credentials); const customToken = await new SignJWT() .setIssuer(credentials.client_email) @@ -249,6 +259,7 @@ export async function getGCPCredentialsAccessToken( const data = await res.json(); if ("id_token" in data) { + const { decodeJwt } = await getJose(); const claims = decodeJwt(data.id_token); return { token: data.id_token, expires: claims.exp as number }; } diff --git a/libs/providers/langchain-google/src/utils/tests/gcp-auth.test.ts b/libs/providers/langchain-google/src/utils/tests/gcp-auth.test.ts new file mode 100644 index 000000000000..e95a472e6b89 --- /dev/null +++ b/libs/providers/langchain-google/src/utils/tests/gcp-auth.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it, vi } from "vitest"; + +describe("gcp-auth", () => { + it("does not import jose eagerly when loading the auth helpers", async () => { + vi.resetModules(); + vi.doMock("jose", () => { + throw new Error("jose should not load during module import"); + }); + + const { normalizeGCPCredentials } = await import("../gcp-auth.js"); + + expect( + normalizeGCPCredentials( + '{"type":"service_account","project_id":"p","private_key_id":"k","private_key":"pem","client_id":"c","client_email":"a@b.c","auth_uri":"https://auth","token_uri":"https://token","auth_provider_x509_cert_url":"https://cert","client_x509_cert_url":"https://client-cert"}' + ) + ).toMatchObject({ + project_id: "p", + private_key_id: "k", + }); + }); + + it("loads jose lazily when a signing helper is called", async () => { + vi.resetModules(); + const importPKCS8 = vi.fn().mockResolvedValue("imported-key"); + + vi.doMock("jose", () => ({ + importPKCS8, + SignJWT: class {}, + decodeJwt: vi.fn(), + })); + + const { getGCPPrivateKey } = await import("../gcp-auth.js"); + + const result = await getGCPPrivateKey({ + type: "service_account", + project_id: "p", + private_key_id: "k", + private_key: "pem", + client_id: "c", + client_email: "a@b.c", + auth_uri: "https://auth", + token_uri: "https://token", + auth_provider_x509_cert_url: "https://cert", + client_x509_cert_url: "https://client-cert", + }); + + expect(result).toBe("imported-key"); + expect(importPKCS8).toHaveBeenCalledWith("pem", "RS256"); + }); +});