11# Claurst Authentication Guide
22
33Claurst needs credentials to call the Anthropic API (or another provider's
4- API). This document covers every supported authentication method, how tokens
5- are stored, how to check and clear credentials, and how to authenticate with
6- non-Anthropic providers.
4+ API). This document covers every supported authentication method, multi-account
5+ profile switching, how tokens are stored, how to check and clear credentials,
6+ and how to authenticate with non-Anthropic providers.
77
88---
99
@@ -14,12 +14,19 @@ Claurst checks for credentials in the following priority order:
14141 . ` --api-key ` flag (highest priority, session-only)
15152 . ` api_key ` field in ` ~/.claurst/settings.json `
16163 . ` ANTHROPIC_API_KEY ` environment variable
17- 4 . Saved OAuth tokens in ` ~/.claurst/oauth_tokens.json `
17+ 4 . Tokens for the ** active Anthropic profile** under
18+ ` ~/.claurst/accounts/anthropic/<id>/oauth_tokens.json `
19+ 5 . Legacy ` ~/.claurst/oauth_tokens.json ` (auto-migrated to a profile on first
20+ read)
1821
1922The first non-empty credential found is used. Provider-specific credentials
2023(OpenAI, Google, etc.) follow the same pattern but use their own environment
2124variables and provider config entries.
2225
26+ Codex (OpenAI ChatGPT subscription) accounts follow a parallel system —
27+ multiple profiles stored under ` ~/.claurst/accounts/codex/<id>/ ` , with the
28+ active profile selected via the account registry.
29+
2330---
2431
2532## Method 1: API Key
@@ -109,7 +116,8 @@ claurst auth login
1091165 . The browser redirects to ` http://localhost:<port>/callback ` with an
110117 authorization code.
1111186 . Claurst exchanges the code for tokens via the token endpoint.
112- 7 . Tokens are saved to ` ~/.claurst/oauth_tokens.json ` .
119+ 7 . Tokens are saved under ` ~/.claurst/accounts/anthropic/<profile-id>/oauth_tokens.json `
120+ and the profile is registered as ** active** in ` ~/.claurst/accounts.json ` .
113121
114122This flow produces a Bearer token (` user:inference ` scope) used directly for
115123API calls.
@@ -121,10 +129,22 @@ claurst auth login --console
121129```
122130
123131This uses the Anthropic Console authorization endpoint. After token exchange,
124- Claurst calls the Console API to create a new API key, stores it in
125- ` ~/.claurst/ oauth_tokens.json` , and uses it as a standard API key for
132+ Claurst calls the Console API to create a new API key, stores it in the
133+ active profile's ` oauth_tokens.json ` , and uses it as a standard API key for
126134subsequent requests (not as a Bearer token).
127135
136+ ### Naming the profile
137+
138+ Add ` --label <name> ` to give the new profile a human-friendly name (otherwise
139+ the id is derived from the JWT email's local-part). This becomes the id you
140+ use when running ` claurst auth switch ` :
141+
142+ ``` bash
143+ claurst auth login --label work
144+ claurst auth login --label personal
145+ claurst auth switch personal
146+ ```
147+
128148### Manual fallback
129149
130150If the browser does not open automatically, Claurst prints the full
@@ -134,6 +154,131 @@ when prompted.
134154
135155---
136156
157+ ## Multi-Account Profiles
158+
159+ Claurst stores ** multiple named accounts per provider** and lets you switch
160+ between them without re-logging-in. Supported providers today: ** Anthropic**
161+ (Claude.ai / Console) and ** Codex** (OpenAI ChatGPT subscription).
162+
163+ This is useful for separating work and personal accounts, juggling
164+ Pro/Max/Team plans, or testing against multiple organizations.
165+
166+ ### On-disk layout
167+
168+ ```
169+ ~/.claurst/
170+ ├── accounts.json # registry (active + metadata)
171+ └── accounts/
172+ ├── anthropic/
173+ │ ├── work/oauth_tokens.json
174+ │ └── personal/oauth_tokens.json
175+ └── codex/
176+ └── work/codex_tokens.json
177+ ```
178+
179+ ` accounts.json ` schema (excerpt):
180+
181+ ``` json
182+ {
183+ "version" : 1 ,
184+ "providers" : {
185+ "anthropic" : {
186+ "active" : " personal" ,
187+ "profiles" : {
188+ "work" : { "id" : " work" , "email" : " kuber@company.example" , "subscription_tier" : " max" , "added_at" : " 2026-05-25T19:00:00Z" },
189+ "personal" : { "id" : " personal" , "email" : " kuber@personal.example" , "subscription_tier" : " pro" , "added_at" : " 2026-05-25T19:05:00Z" }
190+ }
191+ },
192+ "codex" : {
193+ "active" : " work" ,
194+ "profiles" : { "work" : { "id" : " work" , "email" : " kuber@company.example" } }
195+ }
196+ }
197+ }
198+ ```
199+
200+ ### CLI
201+
202+ ` claurst auth ` and ` claurst codex ` are symmetric — same subcommands for both
203+ providers:
204+
205+ ``` bash
206+ # Add accounts (each login becomes its own profile)
207+ claurst auth login # Claude.ai (default)
208+ claurst auth login --console # Console / API-key flow
209+ claurst auth login --label work # name the profile
210+ claurst codex login # ChatGPT/Codex OAuth
211+ claurst codex login --label personal
212+
213+ # Inspect
214+ claurst auth status # show active Anthropic profile
215+ claurst auth list # all Anthropic profiles
216+ claurst codex list # all Codex profiles
217+ claurst accounts # both at once (use --json for JSON)
218+
219+ # Switch the active account
220+ claurst auth switch work
221+ claurst codex switch personal
222+
223+ # Remove a stored profile
224+ claurst auth remove work # delete profile + tokens dir
225+ claurst codex remove personal
226+
227+ # Logout (clears tokens for the active profile)
228+ claurst auth logout
229+ claurst codex logout
230+ ```
231+
232+ ` claurst auth status ` and ` claurst codex status ` exit ` 0 ` when logged in and
233+ ` 1 ` otherwise, so they can drive scripts:
234+
235+ ``` bash
236+ if claurst codex status > /dev/null; then
237+ echo " Codex login present"
238+ fi
239+ ```
240+
241+ ### Slash commands
242+
243+ Inside the interactive REPL the same operations are available as slash
244+ commands — Anthropic is the default, pass ` --codex ` to target Codex:
245+
246+ ```
247+ /login # OAuth login (Claude.ai)
248+ /login --console # API-key flow
249+ /login --codex # add a Codex account
250+ /login --label work # name the new profile
251+ /logout # clear active Anthropic credentials
252+ /logout --codex # clear active Codex credentials
253+ /logout --all # purge every stored Anthropic profile
254+ /accounts # list every stored account
255+ /switch personal # set active Anthropic to "personal"
256+ /switch --codex work # set active Codex to "work"
257+ ```
258+
259+ ` /accounts ` lists every profile with a ` * ` next to the active one and shows
260+ email and subscription tier when known.
261+
262+ ### Identity detection
263+
264+ When you log in, Claurst decodes the JWT id_token (or access token for Codex)
265+ to extract your email and provider-side account_id. If a stored profile
266+ already matches that identity, the existing profile is refreshed instead of
267+ a duplicate being created — re-logging-in the same account is idempotent.
268+
269+ ### Backward compatibility
270+
271+ If you previously used Claurst (with the older single-file storage), your
272+ existing tokens are auto-migrated on first read:
273+
274+ - ` ~/.claurst/oauth_tokens.json ` → ` ~/.claurst/accounts/anthropic/<derived>/oauth_tokens.json `
275+ - ` ~/.claurst/codex_tokens.json ` → ` ~/.claurst/accounts/codex/<derived>/codex_tokens.json `
276+
277+ The legacy files are removed after a successful migration. No manual action
278+ needed.
279+
280+ ---
281+
137282## Method 3: Device Code Flow
138283
139284The device code flow (RFC 8628) is designed for headless or server
@@ -153,12 +298,12 @@ ANTHROPIC_API_KEY="sk-ant-..." claurst --print "summarize the last 10 commits"
153298
154299## Token Storage
155300
156- ### Anthropic OAuth tokens
301+ ### Anthropic OAuth tokens (per profile)
157302
158- Saved to :
303+ Each Anthropic account profile has its own file :
159304
160305```
161- ~/.claurst/oauth_tokens.json
306+ ~/.claurst/accounts/anthropic/<profile-id>/ oauth_tokens.json
162307```
163308
164309The file contains the access token, optional refresh token, expiry timestamp,
@@ -175,12 +320,21 @@ granted scopes, and account email. Example structure:
175320}
176321```
177322
178- The file is written with user-only permissions (` 600 ` on Unix). Do not commit
179- it to version control.
323+ The active profile pointer lives in ` ~/.claurst/accounts.json ` (see
324+ [ Multi-Account Profiles] ( #multi-account-profiles ) ). Files are written with
325+ user-only permissions (` 600 ` on Unix). Do not commit them to version control.
326+
327+ ### Codex tokens (per profile)
328+
329+ ```
330+ ~/.claurst/accounts/codex/<profile-id>/codex_tokens.json
331+ ```
332+
333+ Contains the OpenAI access token, refresh token, account_id, and expiry.
180334
181335### Provider credential store
182336
183- API keys for non-Anthropic providers are stored in:
337+ API keys for non-Anthropic providers without dedicated OAuth flows are stored in:
184338
185339```
186340~/.claurst/auth.json
@@ -203,6 +357,10 @@ This file is keyed by provider ID and contains either an `api` credential
203357}
204358```
205359
360+ > ** Note:** ` ~/.claurst/auth.json ` is the multi-provider credential cache for
361+ > simple API-key providers. It is ** distinct** from ` ~/.claurst/accounts.json ` ,
362+ > which is the multi-account registry for Anthropic/Codex OAuth profiles.
363+
206364---
207365
208366## Checking Authentication Status
252410
253411## Logging Out
254412
413+ By default, ` logout ` removes the ** active** account's tokens and drops that
414+ profile from the registry; other stored profiles are untouched, so a stored
415+ secondary profile becomes the candidate for next selection.
416+
255417``` bash
418+ # Remove the active Anthropic profile
256419claurst auth logout
420+
421+ # Remove the active Codex profile
422+ claurst codex logout
423+
424+ # Or from inside the REPL
425+ /logout
426+ /logout --codex
257427```
258428
259- This removes ` ~/.claurst/oauth_tokens.json ` . API keys set via environment
260- variables or ` settings.json ` are not affected; you must remove those manually.
429+ To purge every stored profile for a provider (and clear any API key in
430+ ` settings.json ` ):
261431
262- To fully clear all stored credentials:
432+ ```
433+ /logout --all # Anthropic
434+ /logout --codex --all # Codex
435+ ```
436+
437+ API keys set via environment variables are not affected by ` logout ` ; remove
438+ them from your shell profile manually.
439+
440+ To delete a specific stored profile without making it active first:
263441
264442``` bash
265- claurst auth logout
266- rm -f ~ /.claurst/auth.json
443+ claurst auth remove work
444+ claurst codex remove personal
267445```
268446
269447---
270448
271449## Token Refresh
272450
273- When Claurst loads OAuth tokens from ` ~/.claurst/oauth_tokens.json ` and the
274- access token is expired, it automatically attempts a silent refresh:
451+ When Claurst loads OAuth tokens for the active profile and the access token
452+ is expired, it automatically attempts a silent refresh:
275453
276- 1 . A ` POST ` request is sent to the token endpoint with the stored refresh token.
277- 2 . If successful, the new access token (and optionally a new refresh token) is
278- written back to ` ~/.claurst/oauth_tokens.json ` .
454+ 1 . A ` POST ` request is sent to the provider's token endpoint with the stored
455+ refresh token.
456+ 2 . If successful, the new access token (and optionally a new refresh token)
457+ is written back to the same per-profile token file.
2794583 . The refreshed token is used for the current session.
280459
281460If the refresh fails (network error, expired refresh token, revoked grant),
282461Claurst falls back to any configured API key. If no API key is available,
283- authentication fails and you must run ` claurst auth login ` again.
462+ authentication fails and you must run ` claurst auth login ` (optionally with
463+ ` --label <name> ` to reuse a profile id) again.
284464
285465---
286466
@@ -393,10 +573,15 @@ claurst --provider llamacpp --api-base http://localhost:8080
393573- Restrict permissions on ` ~/.claurst/ ` to your user only:
394574 ``` bash
395575 chmod 700 ~ /.claurst
396- chmod 600 ~ /.claurst/oauth_tokens.json
576+ chmod 700 ~ /.claurst/accounts
577+ chmod 600 ~ /.claurst/accounts.json
397578 chmod 600 ~ /.claurst/auth.json
398579 chmod 600 ~ /.claurst/settings.json
580+ find ~ /.claurst/accounts -type f -name ' *tokens.json' -exec chmod 600 {} +
399581 ```
582+ Claurst already sets ` 0600 ` on ` accounts.json ` automatically on Unix; the
583+ command above is the belt-and-braces version that also covers the per-
584+ profile token files.
400585- Do not commit ` ~/.claurst/ ` to version control.
401586- Add ` .claurst/ ` to your project's ` .gitignore ` to prevent accidentally
402587 committing project-level settings files that may contain keys.
0 commit comments