mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-12 19:55:19 +02:00
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:
parent
d08bf49d5a
commit
bfcffa7d3a
2 changed files with 19 additions and 3 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue