Rewrite JWT generation using manual construction and Web Crypto API

- Build JWT header and payload manually for exact format control
- Use lower-level importKey/sign from Web Crypto API
- Use RSASSA-PKCS1-v1_5 algorithm directly (RSA+SHA256 = RS256)
- Manual base64url encoding for URL-safe tokens
- Add debug logging to trace JWT generation
- Avoids SignJWT abstraction that was causing algorithm errors
This commit is contained in:
dwindown
2025-12-23 11:17:55 +07:00
parent 0ad50f4b6b
commit 43305a2f16

View File

@@ -30,26 +30,69 @@ interface CreateMeetRequest {
// Function to create JWT and get access token // Function to create JWT and get access token
async function getGoogleAccessToken(serviceAccount: GoogleServiceAccount): Promise<string> { async function getGoogleAccessToken(serviceAccount: GoogleServiceAccount): Promise<string> {
try { try {
// Import JWT and crypto utilities // Use a different JWT library that's more compatible with Google's requirements
const { SignJWT, importPKCS8 } = await import("https://deno.land/x/jose@v4.15.1/index.ts"); const { importKey, sign } = await import("https://deno.land/x/jose@v4.15.1/node/crypto.ts");
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
// Convert private key string to CryptoKey // Convert PEM format private key to CryptoKey
const privateKey = await importPKCS8(serviceAccount.private_key, { const privateKey = await importKey(
algorithm: 'RS256', "pkcs8",
}); // Convert PEM to binary
Uint8Array.from(
atob(serviceAccount.private_key
.replace(/-----BEGIN PRIVATE KEY-----/g, "")
.replace(/-----END PRIVATE KEY-----/g, "")
.replace(/\s/g, "")),
c => c.charCodeAt(0)
),
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
false,
["sign"]
);
// Create and sign JWT // Build JWT header and payload manually
const token = await new SignJWT({ const header = {
alg: "RS256",
typ: "JWT",
};
const payload = {
iss: serviceAccount.client_email, iss: serviceAccount.client_email,
scope: "https://www.googleapis.com/auth/calendar", scope: "https://www.googleapis.com/auth/calendar",
aud: serviceAccount.token_uri, aud: serviceAccount.token_uri,
exp: now + 3600, exp: now + 3600,
iat: now, iat: now,
}) };
.setProtectedHeader({ alg: 'RS256', kid: serviceAccount.private_key_id })
.sign(privateKey); // Encode header and payload
const encodedHeader = btoa(JSON.stringify(header))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
const encodedPayload = btoa(JSON.stringify(payload))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
const signatureInput = `${encodedHeader}.${encodedPayload}`;
// Sign the JWT
const signature = await sign(
"RSASSA-PKCS1-v1_5",
privateKey,
new TextEncoder().encode(signatureInput)
);
// Encode signature
const encodedSignature = btoa(String.fromCharCode(...new Uint8Array(signature)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
const token = `${signatureInput}.${encodedSignature}`;
console.log("Generated JWT (first 100 chars):", token.substring(0, 100));
// Exchange JWT for access token // Exchange JWT for access token
const response = await fetch(serviceAccount.token_uri, { const response = await fetch(serviceAccount.token_uri, {