[FEATURE] Supported way to share/copy credentials between CLAUDE_CONFIG_DIR profiles (macOS Keychain makes file-level workarounds impossible)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single feature request
- [x] I am using the latest version of Claude Code
What problem does this solve?
CLAUDE_CONFIG_DIR is an excellent mechanism for running several harnesses —
different CLAUDE.md, skills, agents, and permission sets — side by side on one
machine. Isolation works well. But credentials are per config dir and there is
no supported way to reuse one identity across profiles, so every new profile
costs an interactive browser login, and every profile is a separate auth
identity thereafter.
For a single human with a single Claude subscription running N harnesses, that is
N logins for one account. It is the main friction in an otherwise clean feature,
and it discourages exactly the use case config-dir isolation enables: cheap,
disposable, experiment-scoped profiles.
Measured behaviour (macOS, Claude Code 2.1.238)
$ claude auth status
"loggedIn": true, "authMethod": "claude.ai"
$ CLAUDE_CONFIG_DIR=~/.harness/claude/swe claude auth status
"loggedIn": false, "authMethod": "none"
There is no file-level workaround, and the failure mode of trying is worse than
the original problem:
- On macOS the authoritative credential is a Keychain generic-password item,
not ~/.claude/.credentials.json. In my case the JSON file was a stale
leftover whose access token had expired a week earlier while the default root
kept working perfectly — so the file is not even a reliable mirror of the live
credential. The item also appears to be namespaced per config dir: the default
root uses Claude Code-credentials, while another config dir gets
Claude Code-credentials-<first 8 hex of sha256 of its absolute path>. There
is therefore no shared record a second profile could be pointed at — each
config dir has its own deterministic slot.
- Symlinking or copying
.credentials.jsoninto a profile makes the session
start and then die with OAuth session expired and could not be, instead of failing honestly at
refreshedauth status. Hand-copying a live
credential between slots carries a separate hazard: if the copy refreshes, it
writes new tokens to its own slot and the source's refresh token may be
revoked server-side — and revocation is not local state, so there is no local
rollback for a broken source login.
- Seeding
userID/oauthAccount/hasAvailableSubscriptioninto the
profile's .claude.json does not authenticate it.
claude auth loginoffers only--claudeai --console --email --sso— no
non-interactive path — so this cannot be scripted for provisioning either.
Net effect: a fresh profile can only be authenticated by a human with a browser,
one profile at a time.
The per-config-dir item naming above was inferred by matching Keychain item names
against hashes of config dir paths, not read from documentation, so I would not
want to script against it — which is much of why I am asking for a supported
command rather than building this myself.
Proposed solution
Any one of these would resolve it; roughly in order of preference:
- An explicit copy/link command, e.g.
claude auth copy --from ~/.claude --to $CLAUDE_CONFIG_DIR
(or claude auth share), which does the Keychain-aware thing on each platform
and handles refresh-token rotation correctly across slots.
- Opt-in credential inheritance: a setting such as
"auth": { "inheritFrom": "~/.claude" } in a profile's settings.json, or an
env var like CLAUDE_CREDENTIALS_DIR that decouples where credentials live
from where config lives. Config isolation and credential isolation are
genuinely separate concerns, and today they are welded together.
- A documented, supported non-interactive login (token in, credential
stored) so profile provisioning can at least be scripted.
Option 2 is the smallest conceptual change: keep the default as today
(credentials follow the config dir), and let a profile explicitly declare that it
shares an identity.
What I'd expect either way
- Failing honestly. If a profile is unauthenticated,
auth statusshould say so
(it does) and nothing should be able to put it into the misleading
"expired session" state via file manipulation.
- Documentation in the
CLAUDE_CONFIG_DIRsection stating plainly that
credentials do not transfer, and what the supported path is. This is currently
learned by trial and error.
Related
- #47661 — the Linux/WSL2 mirror image: credentials there fail to isolate
because there is no Keychain. Both issues point at the same underlying design
question of where credentials live relative to the config dir.
- #80791 —
.claudeat/above cwd treated as project scope over a
CLAUDE_CONFIG_DIR profile. (Unrelated cause; noting it as adjacent
config-dir behaviour. I could not reproduce that one on 2.1.238.)
Environment
- Claude Code 2.1.238
- macOS (Darwin 24.4.0), arm64
- Auth method: claude.ai (team)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗