← Back to PRs

#8225: feat(auth): add forceRefresh option and invalidateOAuthToken for 401 recovery

by arodundef open 2026-02-03 19:23 View on GitHub →
agents stale
## Summary When an OAuth token is revoked server-side before the local `expires` timestamp, the system keeps using the stale cached token until local expiry passes. This causes persistent 401 errors that require manual intervention. ## Changes - Add `forceRefresh?: boolean` parameter to `resolveApiKeyForProfile`, `resolveApiKeyForProvider`, and related functions - Add `invalidateOAuthToken()` function to mark a token as needing refresh (sets `expires: 0`) - Export `invalidateOAuthToken` from `model-auth.ts` for external use ## Usage Callers can now recover from 401 errors by: ```typescript // Option 1: Retry with forced refresh const auth = await resolveApiKeyForProvider({ provider: "anthropic", forceRefresh: true, // Skip local expires check }); // Option 2: Permanently invalidate for next use invalidateOAuthToken({ profileId: "anthropic:claude-cli" }); ``` ## Root Cause The `refreshOAuthTokenWithLock` function checks `Date.now() < cred.expires` and returns cached credentials if they appear valid locally. However, the server may have revoked the token (for security reasons, session limits, etc.) before this timestamp. ## Testing Manual testing performed. The changes are additive and backward-compatible - existing code paths are unchanged when `forceRefresh` is not specified. Fixes #8223 <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR adds a `forceRefresh?: boolean` option to the OAuth API key resolution path so callers can bypass the local `expires` check after a provider-side revocation (e.g., retrying after a 401). It also introduces `invalidateOAuthToken()` which marks a stored OAuth credential as expired (`expires: 0`) to force a refresh on next use, and re-exports that helper through `auth-profiles.ts` / `model-auth.ts` for external callers. The approach is consistent with the existing cached-token flow in `auth-profiles/oauth.ts` (the `Date.now() < cred.expires` fast-path), and the change is additive when `forceRefresh` is omitted. <h3>Confidence Score: 4/5</h3> - This PR is largely safe to merge, with a couple of small issues to address. - Core logic change (skipping the local cache when `forceRefresh` is set) is straightforward and localized. The main concerns are an unused import that may fail lint/TS checks and a potential race condition because invalidation writes the auth store without using the same file lock as refresh. - src/agents/model-auth.ts, src/agents/auth-profiles/oauth.ts <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs