claude login: read the shared Keychain credential when the CLI doesn't namespace by config dir - #20
Conversation
…espace the config dir
|
Hey @rbutera, thanks for the PR! I tried to confirm the premise and got the opposite result, so let me share the test. The PR's claim is that the CLI ignores
Same result on Separate from the repro: reading the shared entry as a fallback is risky even when it works -- that entry is your main Claude Code login, so a miss on the namespaced read would make tokenmaxx silently import the wrong account and then compete with Claude Code over the same OAuth session. Can you share claude --version, how it's installed (npm vs native), and whether your shell sets |
|
Hey @DexterStorey, thanks for digging in and sharing the exact repro, that's genuinely helpful. I think we're both right and looking at two different halves of the flow. Your test plants a credential in the namespaced entry and checks The variable I'd bet on is install method. My setup:
Decisive test if you can: on a NATIVE install, run an actual On the safety point, you're right and I don't want to import the wrong account or fight Claude Code over the OAuth session. Two mitigations already in the PR: the fallback read keeps the same |
|
Yeah, since yesterday my |
|
Hey @pedroapfilho this is a one-time migration cost we should have surfaced. The update removed the dummy Both changes mean the CLIs now want their own login once: run Your tokenmaxx accounts and routing should still work. Also make sure you're on |
|
Hey @rbutera! I ran your decisive test on a setup matching yours exactly (native One theory that fits your evidence: if the CLI on your machine stores the credential in the Data Protection keychain, the security CLI can't see it at all — which would explain both your empty dump and tokenmaxx's miss (and would mean the shared entry you imported might be a stale one, not the fresh login). Could you run this and paste the output? [before pasting, make sure there's nothing sensitive in the result. There shouldn't be.] Then The key line is the second-from-last: if auth status says logged in but that prints non-zero, the credential lives somewhere security can't reach, that's a real bug, and I'd fix tokenmaxx's read rather than add the shared fallback. If the namespaced entry shows up fine, we hunt for a wrapper or env reset on your machine instead. Thank you so much for this back and forth, hopeful we can get to the bottom of this! 🔥 |
Here is my output: |
Problem
tokenmaxx login claudereports success but never stores an account, so the proxy later answers every request with503 no active anthropic account.registerClaudeAccountruns an isolatedclaude auth login --claudeaiunder a temporaryCLAUDE_CONFIG_DIR, thenimportCliCredentialreads the credential back from the macOS Keychain under a service name namespaced by a hash of that config dir (Claude Code-credentials-<hash8>). Current Claude Code (checked on v2.1.220 and v2.1.223) stores its OAuth credential under the plain serviceClaude Code-credentialsregardless ofCLAUDE_CONFIG_DIR, and writes no.credentials.jsonon macOS. Both read branches miss,importCliCredentialthrowsCREDENTIAL_MISSING, and no account row is written. The inner login has already printed "Login successful", so from the outside it looks like it worked and the account simply never appears in the list.Fix
Read whichever store the CLI actually wrote. Try the namespaced service first, so any build that does namespace keeps working, then fall back to the shared
Claude Code-credentialsservice (keeping the same-a <user>filter), then the on-disk file.removeClaudeProfilestill deletes only the namespaced service, so cleanup never touches the user's own shared Claude Code login.Evidence
On v2.1.223,
security dump-keychainshows only the plainClaude Code-credentialsservice with no namespaced variant, and that entry carries theclaudeAiOauthobject the schema expects. The-a <user>filter matters: without it the same service name resolves to a differentmcpOAuth-only entry, so the read keeps the user filter.Tests
Added a regression test: the namespaced lookup misses, the shared service returns the credential, and
registerClaudeAccountpersists it. The existing namespaced-path test still passes.bun test, typecheck, and biome are all clean.The version bump and changelog line came from the repo's own
preparecommit hook; drop or reformat them if you'd rather handle that at release.