Skip to content

fix(ext-claude): refresh expired OAuth token for usage indicator#127

Closed
neumie wants to merge 1 commit into
mainfrom
fix/claude-usage-token-refresh
Closed

fix(ext-claude): refresh expired OAuth token for usage indicator#127
neumie wants to merge 1 commit into
mainfrom
fix/claude-usage-token-refresh

Conversation

@neumie
Copy link
Copy Markdown
Contributor

@neumie neumie commented May 21, 2026

Problem

The Claude usage indicator read the stored accessToken directly and never refreshed it. Once the token expired, the usage API returned 401 Unauthorized and the indicator silently disappeared.

Change

Add an OAuth refresh flow to okena-ext-claude:

  • Read creds with Claude Code's storage precedence — macOS Keychain is authoritative (a present-but-malformed entry does not fall back to the plaintext file), ~/.claude/.credentials.json otherwise — now parsing refreshToken/expiresAt.
  • Refresh proactively within a leeway before expiry, and reactively on 401, via the OAuth token endpoint. 403 and other statuses are treated as non-refreshable failures (refreshing can't fix scope/permission and would churn the single-use token).
  • Persist the rotated token atomically (same-dir temp + rename, 0600); a failed persist is treated as a failed refresh so a rotated token is never lost into a stale file.
  • Serialize concurrent refreshes (other okena instances and Claude Code) with a proper-lockfile-compatible mkdir directory lock: mtime heartbeat, bounded non-spinning acquire, and a race-free claim-by-mtime stale steal.

Notes

  • filetime is declared as a direct dependency but is already in the build tree transitively via gpui, so no new crate is compiled. It's needed to set a directory's mtime cross-platform (std can't on Windows directories).
  • Keychain write exposes the secret on security's argv briefly — documented in-code; security add-generic-password has no stdin input, matching Claude Code's own behavior.

Tests

31 unit tests (clip char-boundary, creds parsing/precedence, atomic-write errors, lock acquire/release/steal/heartbeat, stale detection). cargo test -p okena-ext-claude green; clippy clean for the changed code.

🤖 Generated with Claude Code

@neumie neumie force-pushed the fix/claude-usage-token-refresh branch from 57a2d5c to 6805c4f Compare May 22, 2026 14:05
@neumie neumie requested a review from matej21 May 22, 2026 14:07
The usage indicator read the stored accessToken directly and never
refreshed it, so once the token expired the API returned 401 and the
indicator silently disappeared.

Add a refresh flow:
- Read creds with Claude Code's precedence (macOS Keychain authoritative,
  ~/.claude/.credentials.json fallback), parsing refreshToken/expiresAt.
- Refresh proactively before expiry and reactively on 401 via the OAuth
  token endpoint; persist the rotated token atomically (temp + rename, 0600).
  403 and other statuses are treated as non-refreshable failures.
- Serialize concurrent refreshes (other okena instances and Claude Code)
  with a proper-lockfile-compatible mkdir directory lock: mtime heartbeat,
  bounded acquire, and a race-free claim-by-mtime stale steal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@neumie neumie force-pushed the fix/claude-usage-token-refresh branch from 6805c4f to 6749af3 Compare May 26, 2026 13:09
@neumie neumie closed this May 26, 2026
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