fix: don't warn about expired OAuth tokens with valid refresh tokens (#4593)

OAuth credentials with a refresh token auto-renew on first API call,
so the doctor should not warn about access token expiration when a
refresh token is present. This avoids unnecessary "expired" warnings
that prompt users to re-auth when no action is needed.

Fixes #3032

Co-authored-by: Ayush Ojha <ayushozha@outlook.com>
This commit is contained in:
Ayush Ojha
2026-01-30 07:39:17 -08:00
committed by GitHub
parent da71eaebd2
commit 37e295fc02
2 changed files with 41 additions and 4 deletions

View File

@@ -52,11 +52,39 @@ describe("buildAuthHealthSummary", () => {
);
expect(statuses["anthropic:ok"]).toBe("ok");
expect(statuses["anthropic:expiring"]).toBe("expiring");
expect(statuses["anthropic:expired"]).toBe("expired");
// OAuth credentials with refresh tokens are auto-renewable, so they report "ok"
expect(statuses["anthropic:expiring"]).toBe("ok");
expect(statuses["anthropic:expired"]).toBe("ok");
expect(statuses["anthropic:api"]).toBe("static");
const provider = summary.providers.find((entry) => entry.provider === "anthropic");
expect(provider?.status).toBe("expired");
expect(provider?.status).toBe("ok");
});
it("reports expired for OAuth without a refresh token", () => {
vi.spyOn(Date, "now").mockReturnValue(now);
const store = {
version: 1,
profiles: {
"google:no-refresh": {
type: "oauth" as const,
provider: "google-antigravity",
access: "access",
refresh: "",
expires: now - 10_000,
},
},
};
const summary = buildAuthHealthSummary({
store,
warnAfterMs: DEFAULT_OAUTH_WARN_MS,
});
const statuses = Object.fromEntries(
summary.profiles.map((profile) => [profile.profileId, profile.status]),
);
expect(statuses["google:no-refresh"]).toBe("expired");
});
});

View File

@@ -123,7 +123,16 @@ function buildProfileHealth(params: {
};
}
const { status, remainingMs } = resolveOAuthStatus(credential.expires, now, warnAfterMs);
const hasRefreshToken = typeof credential.refresh === "string" && credential.refresh.length > 0;
const { status: rawStatus, remainingMs } = resolveOAuthStatus(
credential.expires,
now,
warnAfterMs,
);
// OAuth credentials with a valid refresh token auto-renew on first API call,
// so don't warn about access token expiration.
const status =
hasRefreshToken && (rawStatus === "expired" || rawStatus === "expiring") ? "ok" : rawStatus;
return {
profileId,
provider: credential.provider,