OAuth login flow unusable over SSH on macOS — Keychain inaccessible, no reliable remote auth path for Pro/Max subscribers
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
What's Wrong?
Claude Code on macOS stores OAuth credentials exclusively in the macOS Keychain, which is inaccessible from SSH sessions (errSecInteractionNotAllowed, exit code 36). This makes Claude Code unusable over SSH for Pro/Max subscription users unless an undocumented .credentials.json fallback file happens to exist — and there is no documented or reliable way to create or maintain that file.
Environment
Claude Code version: (latest)
Platform: macOS 15.x (Apple Silicon Mac Mini M4 + iMac, both headless via SSH)
SSH client: Termius on iOS
Network: Tailscale overlay network
Subscription: Max (OAuth, not API key)
Setup
Standard remote development workflow:
Mac Mini M4 and iMac, both accessible via Tailscale
iPhone connects via Termius (SSH) over Tailscale from any network
Claude Code runs in the SSH session
Root Cause
Claude Code stores OAuth tokens in the macOS Keychain under the service name "Claude Safe Storage" with account "Claude Key". SSH sessions cannot access Keychain items due to per-item ACLs that require GUI context:
bash# From local terminal — works (with password prompts):
security find-generic-password -s "Claude Safe Storage" -a "Claude Key" -w
From SSH — fails silently:
security find-generic-password -s "Claude Safe Storage" -a "Claude Key" -w
exit code 36 (errSecInteractionNotAllowed)
Claude Code has a file-based credential fallback (~/.claude/.credentials.json) that works over SSH when the file exists. However:
The file does not exist by default. A fresh claude login from a local terminal writes only to Keychain, not to the file.
The file appeared spontaneously during debugging after triggering a Keychain unlock via the security command from a local terminal. The mechanism that creates it is undocumented.
There is no documented way to generate or refresh this file.
Token expiry is unknown. The tokens in the file have an expiresAt field (~24 hours observed). It is unclear whether Claude Code refreshes the file when the token expires, or whether refresh only works through the Keychain (which SSH can't access).
The file could be deleted by Claude Code at any time — updates, cleanup processes, logout — since its lifecycle is undocumented.
Steps to Reproduce
Install Claude Code on macOS, authenticate via claude login (browser OAuth flow completes successfully)
Verify Claude Code works from a local terminal
SSH into the same machine (via Termius, standard SSH, etc.)
Run claude — demands /login
/login attempts to open a browser → fails (no display over SSH)
/login provides a fallback URL (~23,000 characters) → URL is unusable from a mobile terminal (line breaks/truncation corrupt it on copy-paste)
No path forward without physical access to the machine
Partial Workaround Found
If the Keychain is unlocked from a local terminal session (e.g., by running security find-generic-password and entering the Keychain password), Claude Code appears to write ~/.claude/.credentials.json. Subsequent SSH sessions can then read this file and authenticate without /login. This workaround:
Requires physical/GUI access to the machine
Must be done before leaving the machine (no way to do it remotely)
Has unknown durability (token expiry, file persistence)
Is completely undocumented
Expected Behavior
Two things should work, and neither does:
- File-based credentials should be first-class on macOS
When claude login completes, credentials should be written to ~/.claude/.credentials.json in addition to (or instead of) the Keychain, just as they are on Linux. The current behavior — Keychain-only storage that silently fails over SSH — makes Claude Code incompatible with SSH by design.
- Device-code auth flow (RFC 8628)
Authentication should be completable entirely within the terminal via a short device code, following every other major CLI tool:
$ claude /login
No browser detected. Authenticate from another device:
Open: https://claude.ai/device
Code: ABCD-1234
Waiting for authentication... ✓ Authenticated.
This pattern is used by: gh auth login, az login --use-device-code, aws sso login, gcloud auth login, kubectl.
Why This Matters
Pro/Max subscribers pay for Claude Code but cannot use it over SSH — a completely standard development workflow
The only alternative (ANTHROPIC_API_KEY) bypasses the subscription and bills separately — users should not have to pay twice
macOS is a primary development platform and SSH access is routine
The Keychain-only credential storage is a macOS-specific regression — Linux users get file-based credentials by default
Related Issues
#22992 — Device-code auth flow request (RFC 8628)
#9403 — macOS Keychain service name mismatch
#5957 — SSH auth failure after Keychain integration (called out as a regression)
#29816 — SSH sessions require re-login despite valid .credentials.json
#10158 — SSH sessions can't authenticate on macOS (extensive workaround testing)
#7100 — Headless/remote auth documentation
#36807 — Remote control token expiry requiring interactive re-login
#24317 — Frequent re-auth with concurrent sessions
#21765 — Refresh token not used on remote machines
What Should Happen?
Suggested Fix
Immediate: Write credentials to ~/.claude/.credentials.json on claude login on macOS, matching Linux behavior. This is a small change that unblocks all SSH users.
Proper: Implement RFC 8628 device authorization grant. This is a solved problem. Short code, simple URL, works from any device, no clipboard gymnastics.
Error Messages/Logs
n/a
Steps to Reproduce
Steps to Reproduce
Install Claude Code on macOS, authenticate via claude login (browser OAuth flow completes successfully)
Verify Claude Code works from a local terminal
SSH into the same machine (via Termius, standard SSH, etc.)
Run claude — demands /login
/login attempts to open a browser → fails (no display over SSH)
/login provides a fallback URL (~23,000 characters) → URL is unusable from a mobile terminal (line breaks/truncation corrupt it on copy-paste)
No path forward without physical access to the machine
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.92 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗