Implement token caching to avoid unnecessary refresh token calls
- Add expires_at timestamp to OAuth config - Cache access_token in database to reuse across requests - Only refresh token when it expires (after 1 hour) - Use 60-second buffer to avoid using almost-expired tokens - Auto-update cached token after refresh - This fixes the invalid_grant error from excessive refresh calls
This commit is contained in:
@@ -10,6 +10,8 @@ interface GoogleOAuthConfig {
|
|||||||
client_id: string;
|
client_id: string;
|
||||||
client_secret: string;
|
client_secret: string;
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
|
access_token?: string;
|
||||||
|
expires_at?: number; // Timestamp when access_token expires
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateMeetRequest {
|
interface CreateMeetRequest {
|
||||||
@@ -24,22 +26,36 @@ interface CreateMeetRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to get access token from refresh token (OAuth2)
|
// Function to get access token from refresh token (OAuth2)
|
||||||
async function getGoogleAccessToken(oauthConfig: GoogleOAuthConfig): Promise<string> {
|
async function getGoogleAccessToken(oauthConfig: GoogleOAuthConfig): Promise<{ access_token: string; expires_in: number }> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("https://oauth2.googleapis.com/token", {
|
console.log("Attempting to exchange refresh token for access token...");
|
||||||
method: "POST",
|
console.log("Client ID:", oauthConfig.client_id);
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
||||||
body: new URLSearchParams({
|
const tokenRequest = {
|
||||||
client_id: oauthConfig.client_id,
|
client_id: oauthConfig.client_id,
|
||||||
client_secret: oauthConfig.client_secret,
|
client_secret: oauthConfig.client_secret,
|
||||||
refresh_token: oauthConfig.refresh_token,
|
refresh_token: oauthConfig.refresh_token,
|
||||||
grant_type: "refresh_token",
|
grant_type: "refresh_token",
|
||||||
}),
|
};
|
||||||
|
|
||||||
|
console.log("Token request payload:", JSON.stringify({
|
||||||
|
...tokenRequest,
|
||||||
|
client_secret: "***REDACTED***",
|
||||||
|
refresh_token: tokenRequest.refresh_token.substring(0, 20) + "..."
|
||||||
|
}));
|
||||||
|
|
||||||
|
const response = await fetch("https://oauth2.googleapis.com/token", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
body: new URLSearchParams(tokenRequest),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const responseText = await response.text();
|
||||||
|
console.log("Token response status:", response.status);
|
||||||
|
console.log("Token response body:", responseText);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
throw new Error(`Token exchange failed: ${responseText}`);
|
||||||
throw new Error(`Token exchange failed: ${errorText}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -48,7 +64,11 @@ async function getGoogleAccessToken(oauthConfig: GoogleOAuthConfig): Promise<str
|
|||||||
throw new Error("No access token in response");
|
throw new Error("No access token in response");
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.access_token;
|
console.log("Successfully obtained access token");
|
||||||
|
return {
|
||||||
|
access_token: data.access_token,
|
||||||
|
expires_in: data.expires_in || 3600
|
||||||
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error getting Google access token:", error);
|
console.error("Error getting Google access token:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -119,8 +139,36 @@ serve(async (req: Request): Promise<Response> => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get access token using OAuth2 refresh token
|
// Check if we have a valid cached access_token
|
||||||
const accessToken = await getGoogleAccessToken(oauthConfig);
|
let accessToken: string;
|
||||||
|
const now = Math.floor(Date.now() / 1000); // Current time in seconds
|
||||||
|
|
||||||
|
if (oauthConfig.access_token && oauthConfig.expires_at && oauthConfig.expires_at > now + 60) {
|
||||||
|
// Token is still valid (with 60 second buffer)
|
||||||
|
console.log("Using cached access_token (expires at:", new Date(oauthConfig.expires_at * 1000).toISOString(), ")");
|
||||||
|
accessToken = oauthConfig.access_token;
|
||||||
|
} else {
|
||||||
|
// Need to refresh the token
|
||||||
|
console.log("Access token expired or missing, refreshing...");
|
||||||
|
const tokenData = await getGoogleAccessToken(oauthConfig);
|
||||||
|
accessToken = tokenData.access_token;
|
||||||
|
|
||||||
|
// Update the cached token in database with new expiry
|
||||||
|
const newExpiresAt = now + tokenData.expires_in;
|
||||||
|
const updatedConfig = {
|
||||||
|
...oauthConfig,
|
||||||
|
access_token: accessToken,
|
||||||
|
expires_at: newExpiresAt
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save updated config back to database
|
||||||
|
await supabase
|
||||||
|
.from("platform_settings")
|
||||||
|
.update({ google_oauth_config: JSON.stringify(updatedConfig) })
|
||||||
|
.eq("id", settings.id);
|
||||||
|
|
||||||
|
console.log("Updated cached access_token in database");
|
||||||
|
}
|
||||||
console.log("Got access token");
|
console.log("Got access token");
|
||||||
|
|
||||||
// Build event data
|
// Build event data
|
||||||
|
|||||||
Reference in New Issue
Block a user