Most Claude Code setups end up the same way: a GitHub PAT pasted into ~/.zshrc, a Linear API key in a .env, a Stripe restricted key somewhere in a config file you forget about. Six months later half of them are stale, none of them refresh, and you're not sure which token belongs to which account.
This post replaces that pile with authsome. The pitch is that you log into each provider once, the broker keeps the tokens fresh, and Claude Code makes its normal API calls without ever holding the raw secret.
I'm not going to pretend the setup is one command. The first time you go through it, the GitHub OAuth-app bit takes a few minutes and the mitmproxy CA cert needs to be trusted. After that, day two is a single authsome login per service and Claude Code just works.
What we're building
Claude Code makes a request to api.github.com. The local proxy intercepts it, looks up the right token, slots it into the Authorization header, and forwards. Claude Code's process never sees the real token. Your shell never sees it either.
Install authsome
Python 3.13+ required. Pick whichever fits how you manage Python tools:
pip install authsome
Or run without installing:
uvx authsome --version
Then sanity-check:
authsome init
authsome doctor
init creates ~/.authsome/, generates a master key, and registers your local profile. doctor runs health checks on the directory layout, encryption availability, and bundled provider parsing. All checks should pass before you do anything else.
Connect GitHub
GitHub doesn't support Dynamic Client Registration, so the first time around you have to register your own OAuth app. It's a four-minute one-time thing:
- Open github.com/settings/developers → New OAuth App.
- Name: anything.
authsome (local)is what I use. - Homepage URL: any URL.
http://localhost:3000works. - Authorization callback URL:
http://127.0.0.1:7998/auth/callback/oauth· this one matters. Any other value fails withredirect_uri_mismatchat login time. - Check Enable Device Flow if you'll ever log in from an SSH session.
- Register the app. Copy the Client ID. Click Generate a new client secret and copy that too.
Now from the terminal:
authsome login github
Authsome opens a local form at http://127.0.0.1:7998 asking for the Client ID and Client Secret. Paste them. They're stored encrypted under your profile. A second tab opens to GitHub's authorization page. Approve. The terminal prints Successfully logged in to github (default).
Verify:
authsome list
GitHub should show as connected. From now on, every subsequent login or refresh reuses the stored client credentials. You won't see the form again unless you authsome revoke github.
Connect Linear
Linear's MCP endpoint supports DCR, but the API-key path is the one most people start with. Same login command:
authsome login linear
A local form opens asking for the API key. Get one from linear.app/settings/api and paste. The key is stored encrypted.
The reason the prompt is in the browser and not the terminal: a terminal-pasted key ends up in ~/.zsh_history and shows up in any process listing while you're typing. The local form posts straight to the daemon over loopback and the value never touches your shell.
Connect Stripe
Same pattern:
authsome login stripe
For Claude Code workloads I strongly recommend a restricted key (rk_test_... or rk_live_...) scoped to the minimum the agent needs. Read access to customers and payments is enough for most read-only flows. Avoid the unrestricted sk_live_* unless the agent actually needs to charge cards.
Sanity check all three
authsome list
You should see three rows, each connected. For more detail (per-provider config, scopes, expiry, default connection name):
authsome inspect github
authsome inspect linear
authsome inspect stripe
Point Claude Code at the broker
Two viable patterns. Proxy is the secure default. Export is the fallback for environments where the proxy doesn't work cleanly.
Launch Claude Code under authsome run:
authsome run -- claude
This sets HTTP_PROXY / HTTPS_PROXY to authsome's local proxy in Claude Code's environment. Claude Code's HTTP client (and any subprocess it spawns) honors the proxy variables. Outbound calls to api.github.com, api.linear.app, api.stripe.com get the right Authorization header injected at the proxy boundary.
In the child process, the placeholder env var is what's set:
authsome run -- env | grep -E 'OPENAI|GITHUB|PROXY'
# HTTP_PROXY=http://127.0.0.1:<port>
# HTTPS_PROXY=http://127.0.0.1:<port>
# OPENAI_API_KEY=authsome-proxy-managed
The real key isn't anywhere in the process environment. Substitution happens at the proxy.
The CA cert thing (do this once)
authsome run uses mitmproxy under the hood to do HTTPS interception. mitmproxy needs its CA certificate trusted on your machine, otherwise every HTTPS call from Claude Code fails with CERTIFICATE_VERIFY_FAILED.
mitmproxy ships a built-in installer. While authsome run is alive, open a browser on the same machine and go to http://mitm.it. The page detects your OS and gives you a one-click installer for macOS, Windows, Linux, iOS, and Android. This is the path mitmproxy's own docs recommend.
If you can't use the browser installer (headless box, automated provisioning), install the CA manually:
sudo security add-trusted-cert \
-d -r trustRoot \
-k /Library/Keychains/System.keychain \
~/.mitmproxy/mitmproxy-ca-cert.pem
System-wide trust. Confirm in Keychain Access → System → Certificates.
The mitmproxy CA can sign certs for any domain Claude Code talks to. Don't share ~/.mitmproxy/mitmproxy-ca-cert.pem or copy it onto someone else's machine. Anyone with the cert can MITM you the same way the proxy does.
If you're using Python tooling under authsome run and still seeing TLS errors after installing the CA, the request library is checking its own bundled CA store. The fix is to point it at the system store with REQUESTS_CA_BUNDLE or SSL_CERT_FILE. The Proxy networking troubleshooting page has the per-language snippets.
Multiple accounts on the same provider
Two GitHub accounts (personal and work) is the case that breaks the env-var model entirely. With authsome, each account is a named connection within the same provider:
authsome login github --connection personal
authsome login github --connection work
Each login runs its own browser flow, against the right account, with its own access token. The OAuth client_id and client_secret are shared (it's the same OAuth app), but each connection has an independent token bound to its own scopes.
Read from a specific connection:
authsome get github --connection work --field status
authsome export github --connection work --format env
The proxy uses the default connection for each provider on every request. Per-request connection selection inside authsome run is on the roadmap; today, if you need the agent to use a different connection, you change which one is default first:
authsome set-default github work
authsome run -- claude
Or, the older form that the docs still document:
authsome login github --connection work --force
--force overwrites the existing default. Both achieve the same result.
If you want total isolation between two credential sets (not just "two accounts on the same provider", but "personal Slack should never see work tooling"), look at profiles instead of connections. Profiles are a separate identity handle with its own KV store; switching the active profile switches the entire credential set. The Profiles vs connections page explains the rule of thumb in detail.
When it breaks
A small fix list for the things that bit me in week one:
TLS errors on every HTTPS call. You skipped the mitmproxy CA install. Open http://mitm.it and run the installer, or use the per-OS commands above.
"Authentication failed" from one provider. Token expired or got revoked. Re-run authsome login <provider>; the stored client credentials are reused so it's a single command.
Claude Code makes calls but the broker logs are empty. Claude Code didn't honor HTTP_PROXY. You probably launched it from outside the authsome run wrapper. Re-launch via authsome run -- claude, or fall back to the env-export pattern.
Claude Code is using the wrong GitHub account. The default connection isn't the one you want. Either authsome set-default github work to flip the default, or use authsome export github --connection work --format env + eval to inject the specific one into the env.
Want to see what the broker is doing. authsome log shows the structured audit entries (every login, every refresh, every credential read). authsome log --raw shows the raw client debug log. authsome ui opens the daemon dashboard in your browser if you'd rather click than grep.
What you've built
$ authsome list
github connected oauth2
linear connected api_key
stripe connected api_key
$ authsome run -- claude
# Claude Code is now talking to all three services
# without holding a single real credential.
Zero tokens in ~/.zshrc. Refresh handled automatically by the daemon. Multi-account modeled explicitly via --connection. Visibility into every credential read via authsome log.
Same pattern works for the rest of authsome's bundled providers (44 at the time of writing, including OpenAI, Anthropic, Slack, Notion, Resend, Vercel, Cloudflare). The flow is always authsome login <name>, browser opens, approve, done. The full list is at authsome.ai/docs/reference/bundled-providers.
Next steps
Further reading
Claude Code: the production-ready setup guide
Beyond the install screen. Skills, MCP servers, multi-account auth, CI usage, and the credential layer that keeps it from leaking. The setup I wish someone had handed me on day one.
Read postMay 4, 2026Managing multiple GitHub accounts for AI agents
Work and personal GitHub identities, both reachable from the same shell, with the right one picked per task. The actual mechanics, not the wishful version.
Read postMay 1, 2026Headless agent OAuth: the device code flow explained
How OAuth2 device authorization works, why it's the right pattern for SSH sessions and CI runners, and what the RFC doesn't quite tell you.
Read post