deep-research / src /utils /vertexAuth.ts
Leon4gr45's picture
Deploy app
c16e487 verified
export interface GoogleCredentials {
/**
* The client email for the Google Cloud service account. Defaults to the
* value of the `GOOGLE_CLIENT_EMAIL` environment variable.
*/
clientEmail: string;
/**
* The private key for the Google Cloud service account. Defaults to the
* value of the `GOOGLE_PRIVATE_KEY` environment variable.
*/
privateKey: string;
/**
* Optional. The private key ID for the Google Cloud service account. Defaults
* to the value of the `GOOGLE_PRIVATE_KEY_ID` environment variable.
*/
privateKeyId?: string;
}
// Convert a string to base64url
const base64url = (str: string) => {
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
};
const decodeBase64 = (base64: string) => {
return Buffer.from(base64, "base64").toString("utf-8");
};
const importPrivateKey = async (pemKey: string) => {
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
// Remove header, footer, and any whitespace/newlines
const pemContents = pemKey
.replace(pemHeader, "")
.replace(pemFooter, "")
.replace(/\s/g, "");
// Decode base64 to binary
const binaryString = decodeBase64(pemContents);
// Convert binary string to Uint8Array
const binaryData = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
binaryData[i] = binaryString.charCodeAt(i);
}
return await crypto.subtle.importKey(
"pkcs8",
binaryData,
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
true,
["sign"]
);
};
const buildJwt = async (credentials: GoogleCredentials) => {
const now = Math.floor(Date.now() / 1000);
// Only include kid in header if privateKeyId is provided
const header: { alg: string; typ: string; kid?: string } = {
alg: "RS256",
typ: "JWT",
};
if (credentials.privateKeyId) {
header.kid = credentials.privateKeyId;
}
const payload = {
iss: credentials.clientEmail,
scope: "https://www.googleapis.com/auth/cloud-platform",
aud: "https://oauth2.googleapis.com/token",
exp: now + 3600,
iat: now,
};
const privateKey = await importPrivateKey(credentials.privateKey);
const signingInput = `${base64url(JSON.stringify(header))}.${base64url(
JSON.stringify(payload)
)}`;
const encoder = new TextEncoder();
const data = encoder.encode(signingInput);
const signature = await crypto.subtle.sign(
"RSASSA-PKCS1-v1_5",
privateKey,
data
);
const signatureBase64 = base64url(
String.fromCharCode(...new Uint8Array(signature))
);
return `${base64url(JSON.stringify(header))}.${base64url(
JSON.stringify(payload)
)}.${signatureBase64}`;
};
/**
* Generate an authentication token for Google Vertex AI in a manner compatible
* with the Edge runtime.
*/
export async function generateAuthToken(credentials: GoogleCredentials) {
try {
const creds = credentials;
const jwt = await buildJwt(creds);
const response = await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: jwt,
}),
});
if (!response.ok) {
throw new Error(`Token request failed: ${response.statusText}`);
}
const data = await response.json();
return data.access_token;
} catch (error) {
throw error;
}
}