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:
@@ -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, {
|
||||||
|
|||||||
Reference in New Issue
Block a user