Skip to content

Release 1.1.1: clear re-auth prompt when Claude refresh token is dead - #7

Merged
melvinfoo6776 merged 1 commit into
mainfrom
claude/nervous-kepler-9e7c72
Jun 26, 2026
Merged

melvinfoo6776 merged 1 commit into
mainfrom
claude/nervous-kepler-9e7c72

Conversation

@melvinfoo6776

Copy link
Copy Markdown
Owner

Problem

When the stored Claude refresh token is permanently invalid — the OAuth token endpoint returns invalid_grant (token revoked, rotated away by a login elsewhere, or expired) — the app could not recover and gave the user no useful guidance:

  • The bridge started a 5-minute backoff (meant for transient failures), so every subsequent Refresh Claude Login click returned a generic Refresh backing off after a recent failure.
  • The button cannot fix a dead refresh token at all — the only fix is claude auth login — but nothing told the user that.

This was hit in practice: a stale Keychain credential showed "Claude login: Expired" with no path forward from the UI.

Fix

Bridge (codex_usage_server.py)

  • On invalid_grant, do not start the transient backoff (waiting can't revive a dead token).
  • Remember the dead refresh token and return reauth_required: true; the next refresh short-circuits without hitting the OAuth endpoint again (no hammering).
  • State self-heals: once a different credential is stored (after claude auth login) or a valid token is observed, the flag clears.
  • GET /claude/status now exposes reauth_required.

App (BridgeService.swift)

  • Refresh Claude Login now reports "Claude session expired. Run claude auth login … to sign in again" with restartRecommended: false, instead of wrongly suggesting a bridge restart.

Release

  • Version bump 1.1 → 1.1.1 (build 1 → 2).
  • CHANGELOG entry.

Tests

Added test_invalid_grant_flags_reauth_and_skips_backoff: verifies no backoff is set, the dead token short-circuits the second attempt (endpoint hit once), status reports reauth_required, and the state self-heals when a fresh credential appears. All 10 bridge tests pass; secret scan clean.

🤖 Generated with Claude Code

When the stored Claude refresh token is permanently invalid (OAuth
invalid_grant — revoked, rotated away, or expired), the bridge previously
started a five-minute backoff and "Refresh Claude Login" surfaced a generic
"backing off after a recent failure". Refresh can never succeed in that state,
so the message was misleading and gave the user no way forward.

- Bridge: on invalid_grant, skip the transient backoff, remember the dead
  refresh token, and return reauth_required so the next refresh short-circuits
  without hammering the OAuth endpoint. State self-heals once a new credential
  is stored; GET /claude/status now exposes a reauth_required flag.
- App: "Refresh Claude Login" tells the user to run `claude auth login` instead
  of wrongly recommending a bridge restart.
- Tests: cover the invalid_grant path (no backoff, short-circuit, self-heal).
- Bump app version to 1.1.1 (build 2) and document in CHANGELOG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@melvinfoo6776
melvinfoo6776 merged commit f8b63d1 into main Jun 26, 2026
1 check passed
@melvinfoo6776
melvinfoo6776 deleted the claude/nervous-kepler-9e7c72 branch June 26, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant