fix(google-oauth): request offline access so BYOK tokens refresh

BYOK never requested access_type=offline/prompt=consent so no refresh token was issued and tokens died after ~1h; also stop handing back expired tokens and extend the connect timeout to 10m.
This commit is contained in:
Gagancreates 2026-06-09 01:25:09 +05:30
parent d08bf49d5a
commit bfcffa7d3a
2 changed files with 19 additions and 3 deletions

View file

@ -420,15 +420,23 @@ export async function connectProvider(provider: string, credentials?: { clientId
scope: scopes.join(' '),
code_challenge: codeChallenge,
state,
// Google only returns a refresh_token when offline access is requested,
// and only re-issues one when re-consent is forced. Without these, a
// BYOK token expires after ~1h with no way to refresh (it goes stale and
// every Google call — including the Picker — starts failing).
...(provider === 'google' ? { access_type: 'offline', prompt: 'consent' } : {}),
});
// Set timeout to clean up abandoned flows (2 minutes)
// Set timeout to clean up abandoned flows. Generous (10 min) because a
// first-time connect can involve creating/locating OAuth credentials in
// the Cloud Console mid-flow; a short window tears down the callback
// server before the user finishes consent, silently dropping the token.
const cleanupTimeout = setTimeout(() => {
if (activeFlow?.state === state) {
console.log(`[OAuth] Cleaning up abandoned OAuth flow for ${provider} (timeout)`);
cancelActiveFlow('timed_out');
}
}, 2 * 60 * 1000);
}, 10 * 60 * 1000);
activeFlow = {
provider,

View file

@ -200,8 +200,16 @@ export async function getGoogleDocsConnectionStatus(): Promise<{
* Picker (file selection happens client-side; the app never lists Drive).
*/
export async function getGoogleAccessToken(): Promise<string | null> {
// getClient() refreshes an expired token when it can. If it returns a token
// that's still past expiry, the refresh failed (e.g. no refresh_token) — hand
// back null rather than a dead token, so the UI prompts a reconnect instead
// of silently passing an expired token to the Picker (which 403s on it).
const auth = await GoogleClientFactory.getClient();
return auth?.credentials?.access_token ?? null;
const token = auth?.credentials?.access_token ?? null;
if (!token) return null;
const expiry = auth?.credentials?.expiry_date;
if (typeof expiry === 'number' && expiry <= Date.now()) return null;
return token;
}
/** Import a Google Doc as a local .docx and register the link. */