fix: update credential handling to prioritize primary service for token refresh#99
fix: update credential handling to prioritize primary service for token refresh#99errhythm wants to merge 5 commits intogriffinmartin:mainfrom
Conversation
|
@griffinmartin I see a lot of great changes made and I believe we can use some of the features we have here. Do you want me to update this? |
|
Hey @errhythm, yeah I'd love for you to update this! There's some really useful stuff here, especially the Bug 1 fix for suffixed keychain accounts and the email display in the account selector. A few things to keep in mind for the rebase:
The Bug 1 fix, configDir passthrough, keychainSuffixForDir, and the regex tightening are all good to go as-is. Looking forward to the update! |
…en refresh
This commit introduces a bug fix for the credential refresh logic, ensuring that if the active account is a suffixed entry and its credentials are stale, the system will fall back to the primary service ("Claude Code-credentials") to retrieve updated tokens. Additionally, the PRIMARY_SERVICE constant is now consistently exported across relevant files to maintain clarity and prevent duplication.
This commit updates the `refreshViaCli` function to accept an optional `configDir` parameter, allowing the CLI to read from and write back to the correct account directory. It also improves logging for credential refresh attempts and modifies the `buildAccountLabels` function to append email addresses when available. Additionally, new tests are added to ensure proper handling of the `CLAUDE_CONFIG_DIR` environment variable across different platforms.
42d47bf to
9cba1cf
Compare
… improvements This commit introduces a mock for the child process in the credential tests, allowing for better isolation during testing. Additionally, it improves the formatting of temporary directory creation and JSON file writing in the tests for better readability and consistency. The changes ensure that the tests remain robust while simulating the necessary environment for credential handling.
446b7e3 to
a72a12a
Compare
This commit modifies the keychain test file to replace instances of `process.platform` with a hardcoded value of `"darwin"`. This change ensures that the tests can run consistently in a controlled environment, improving test reliability and isolation.
|
@griffinmartin Please review if it is correct. |
…ting Update all config examples in README.md and installation.md to use `opencode-claude-auth@latest` so OpenCode always fetches the newest version on startup. Add a troubleshooting entry and "Updating the plugin" section for the "You're out of extra usage" / "Third-party apps" 400 error that affected users on stale cached versions. The fix: ensure @latest in config, clear ~/.cache/opencode/packages/opencode-claude-auth*, restart OpenCode, and re-authenticate if needed. Closes griffinmartin#145, closes griffinmartin#99 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs were found by analyzing debug logs from users hitting the "Claude Code credentials are unavailable or expired" error.
Bug 1 — macOS / keychain (hard failure)
Users with multiple Claude Code accounts have a suffixed active account (e.g. Claude Code-credentials-b28bbb7c). When the token expires, the Claude CLI is invoked to refresh it and succeeds, but the CLI writes the new token back to the primary Keychain entry (Claude Code-credentials), not the suffixed one. refreshAccount(target.source) re-reads the suffixed entry, which still holds the old expiresAt, the post-refresh validity check fails, and refresh_exhausted → credentials_unavailable is emitted. The user sees:
Fix: if the suffixed account re-read is still stale after a CLI refresh, fall back to reading the primary entry. The fallback is gated on target.source.startsWith(PRIMARY_SERVICE + "-") so it only fires for suffixed accounts.
Bug 2 — all platforms (infinite refresh loop) (Potentially fixes #89)
After refreshIfNeeded returns fresh credentials, getCachedCredentials stores them in accountCacheMap with a 30-second TTL. When the cache expires, getActiveAccount() returns the same ClaudeAccount object from allAccounts — whose credentials.expiresAt was never updated. So refreshIfNeeded sees the original stale expiry, runs another full CLI refresh, and the cycle repeats every 30 seconds indefinitely. Visible in logs as refresh_needed firing with the same expiresAt value on every cache miss, hours after the token was successfully refreshed.
Fix: after a successful refresh, target.credentials is updated in-place on the ClaudeAccount object so subsequent cache misses see the correct expiresAt.
Also now
opencode auth loginwill show the user account details instead of just Claude Pro/Team/Max◆ Select which Claude Code account to use:
│ ● Claude Pro: john.doe@gmail.com (Claude Code-credentials)
│ ○ Claude Team: john@acme.com