Skip to content

fix(oauth): scope the Keychain lookup to the current user's item - #185

Open
Devin345458 wants to merge 1 commit into
KarpelesLab:masterfrom
Devin345458:fix/keychain-account-scoped-lookup
Open

fix(oauth): scope the Keychain lookup to the current user's item#185
Devin345458 wants to merge 1 commit into
KarpelesLab:masterfrom
Devin345458:fix/keychain-account-scoped-lookup

Conversation

@Devin345458

Copy link
Copy Markdown
Contributor

find-generic-password -s "Claude Code-credentials" -w returns whichever item the Keychain yields first, and the service name is not unique.

On this machine Claude Code has left a stray acct="unknown" item holding only mcpOAuth next to the real acct="<login name>" one. importCredentials reads the stray item, finds no claudeAiOauth, and reports no credentials — while a valid login sits in the item beside it.

Reproduced against the real Keychain, before and after:

before (master) : keys=[mcpOAuth]                 token=MISSING
after  (patch)  : keys=[mcpOAuth, claudeAiOauth]  token=PRESENT

The fix

Ask for the current user's item first, fall back to the existing service-only lookup, and take the first payload that actually carries a token. That also skips a present-but-blank claudeAiOauth left behind by a logout.

Behaviour is unchanged on machines with a single item: the scoped lookup hits it, and if the account name doesn't match, the fallback is the exact call master makes today.

Tests

readKeychainCredentials is now exported and takes { exec, username }, so it can be injected the way importCredentials already is. Six cases added to test/import-credentials.test.js covering lookup order, the stray-item case, the blank-token case, fallback, the no-username path, and error propagation.

531 tests pass, eslint clean.

The service name is not unique, so `find-generic-password -s NAME -w`
returns whichever item the Keychain yields first. Where Claude Code has
left a stray acct="unknown" item holding only mcpOAuth, the import reads
that one and reports no credentials while a valid login sits in the item
next to it.

Ask for the current user's item first, fall back to the service-only
lookup, and take the first payload that actually carries a token — which
also skips a present-but-blank claudeAiOauth left behind by a logout.
Copilot AI lite review requested due to automatic review settings August 21, 2026 15:07

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Scopes macOS Keychain credential lookup to the current user while preserving service-only fallback behavior.

Changes:

  • Adds ordered, token-aware Keychain lookups.
  • Exports and injects readKeychainCredentials for testing.
  • Adds regression coverage for lookup and fallback scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Summary
test/import-credentials.test.js Tests scoped lookup, fallback, blank credentials, and errors.
src/oauth.js Implements scoped Keychain lookup and token selection.
Suppressed comments (1)

test/import-credentials.test.js:96

  • The args.includes('-a') branch returns CREDS for the scoped lookup, so readKeychainCredentials returns immediately and never executes the service-only { mcpOAuth: {} } branch. This test therefore does not cover skipping a credential-less first payload, which is the stray-item behavior it is intended to protect. Swap the fixtures so the scoped call returns the no-credential payload and the fallback returns CREDS.
  const exec = async (_bin, args) => ({
    stdout: JSON.stringify(args.includes('-a') ? { claudeAiOauth: CREDS } : { mcpOAuth: {} }),
  });

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

2 participants