Reliable Claude Authentication Strategy
Status: Deferred — needs design work
Problem
Section titled “Problem”jackin’ currently optimizes for the simplest happy path: if the operator is already authenticated with Claude Code on the host, a new agent container can start pre-authenticated without requiring /login inside the container.
That convenience is real, but the current forwarding model is fragile for long-lived or concurrent sessions.
Today jackin’ forwards Claude Code subscription auth by copying OAuth session state into the agent’s persisted state directory:
- host
~/.claude.jsonis copied to~/.jackin/data/<container-name>/.claude.json - host credentials are copied to
~/.jackin/data/<container-name>/.claude/.credentials.json - those files are then bind-mounted back into the container on launch
That means every long-lived agent instance can end up with its own private copy of Claude Code auth state.
This is workable for short-lived single-session usage, but it interacts badly with OAuth refresh-token rotation when multiple Claude Code sessions are active across the host and several jackin containers.
The user-visible failure mode is exactly the one operators report:
Please run /login · API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid authentication credentials"}}Why It Matters
Section titled “Why It Matters”- jackin’s value proposition is smooth operator UX when the host is already set up
- auth that works today but fails tomorrow is worse than auth that is explicit from the start
- operators often keep multiple agent sessions open across projects, workspaces, and clone containers
- a copied OAuth session is not a stable runtime contract when refresh tokens rotate independently
- repeated
/loginprompts are especially painful inside isolated runtimes because browser-driven recovery breaks flow
Current State
Section titled “Current State”Status as of the Token-mode release:
sync— overwrite container auth from host on each launch when host auth exists (default since thesync-default release)ignore— never forward host auth; require in-container/logintoken— injectCLAUDE_CODE_OAUTH_TOKENfrom the resolved operator env and leave the agent state directory empty; recommended for long-lived or concurrent sessions (delivered in PR 3 of this series)
Claude Code’s own authentication model also matters here. The official docs support several credential sources with a defined precedence order, including:
- subscription OAuth credentials from
/login CLAUDE_CODE_OAUTH_TOKENfromclaude setup-token- API-key based terminal auth (
ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKEN,apiKeyHelper)
That means jackin is not limited to copying the default /login credential store. There is room for a more runtime-friendly auth contract.
Keep the current convenience goal:
- If the operator is already authenticated locally, jackin should be able to start an agent without requiring manual
/logininside the container. - The chosen auth mechanism should remain reliable across restarts and normal day-to-day usage.
- The operator should understand which mode they are using and what tradeoffs it carries.
- Multiple active jackin sessions should degrade gracefully rather than surprising the operator with intermittent 401s.
Non-Goals
Section titled “Non-Goals”- Replacing Claude Code’s upstream authentication implementation
- Claiming jackin can fully prevent OAuth refresh races inside Claude Code itself
- Building a general secrets platform in this design pass
- Solving auth for non-Claude runtimes before the Claude-specific path is made robust
Design Options
Section titled “Design Options”Option 1: Keep copy as the primary workflow
Section titled “Option 1: Keep copy as the primary workflow”This is the current behavior.
Pros:
- smallest implementation
- zero new operator concepts
- preserves the current docs and config model
Cons:
- stale container-local auth is easy to accumulate
- copied auth can diverge from host auth immediately after first launch
- concurrent long-lived sessions remain fragile
- the failure mode is confusing because the host may still be authenticated when the container is not
Option 2: Treat sync as the recommended default
Section titled “Option 2: Treat sync as the recommended default”This keeps the current copied-session design but changes the guidance and possibly the default.
Pros:
- reduces startup drift significantly
- simpler than inventing a new auth mode
- preserves the operator mental model of “use my host auth”
Cons:
- does not solve mid-session drift for already-running containers
- still depends on copied subscription OAuth state
- a second runtime can still invalidate auth later through token rotation
Option 3: Add a long-lived token mode based on CLAUDE_CODE_OAUTH_TOKEN
Section titled “Option 3: Add a long-lived token mode based on CLAUDE_CODE_OAUTH_TOKEN”Claude Code documents a supported one-year OAuth token flow via:
claude setup-tokenThe output can be set as:
export CLAUDE_CODE_OAUTH_TOKEN=...jackin could add a first-class auth mode that injects this token into the runtime instead of copying /login session files.
Pros:
- better fit for non-interactive or semi-persistent runtimes
- avoids bind-mounting a cloned subscription session into every container
- cleaner operator contract for “I already authorized locally, now launch agents”
- aligns with an officially documented Claude Code auth path for scripts and CI-like environments
Cons:
- requires new operator setup and storage decisions
- still needs careful handling so the token is not leaked into images, logs, or persisted state accidentally
- may not cover every Claude Code feature that assumes interactive subscription OAuth state
Option 4: Add runtime warnings and recovery UX, but keep current auth mechanics
Section titled “Option 4: Add runtime warnings and recovery UX, but keep current auth mechanics”This focuses on detection and clearer messaging rather than changing the auth primitive.
Possible improvements:
- warn when launching multiple forwarded-auth containers for the same account
- warn when
copyis selected for a pre-existing container family - surface a targeted recovery hint when a container hits 401 (
switch to sync,purge, oruse token mode) - document concurrent-session risk explicitly in the auth guide
Pros:
- low-risk improvements
- better operator diagnosis
- valuable even if a stronger auth mode is added later
Cons:
- does not solve the underlying drift problem by itself
Recommendation
Section titled “Recommendation”Delivered (three-PR series):
syncis the default auth-forwarding mode.- A workspace-scoped env resolver populates
CLAUDE_CODE_OAUTH_TOKENfromop://references or host env vars. tokenmode uses that resolved env instead of bind-mounting/loginsession state. Token mode is now the recommended choice for long-lived or concurrent jackin sessions;syncremains a good default for ad-hoc single-session usage.
Suggested Operator UX
Section titled “Suggested Operator UX”Examples:
jackin config auth set syncjackin config auth set ignorejackin config auth set tokenPotential setup helper:
jackin config auth setup-tokenThat command could either:
- guide the operator through
claude setup-token - or simply explain how to generate and register the token manually
Config
Section titled “Config”Suggested shape:
[claude]auth_forward = "token"If token mode needs an explicit token source, keep it operator-owned rather than agent-owned.
Possible configuration directions:
[claude]auth_forward = "token"oauth_token_source = "keychain"or:
[claude]auth_forward = "token"oauth_token_env = "JACKIN_CLAUDE_OAUTH_TOKEN"The exact storage contract needs a separate security pass.
Runtime messaging
Section titled “Runtime messaging”Launch output should clearly state the active auth mode, for example:
claude auth: forwarded host session (copy)claude auth: forwarded host session (sync)claude auth: long-lived OAuth tokenclaude auth: none (manual /login required)
If multiple containers are active with copied session auth, jackin should say so directly.
Security Notes
Section titled “Security Notes”Token mode is better for reliability, but it does not remove the need for careful secret handling.
Requirements:
- never bake the token into derived images
- never print it in logs or launch summaries
- avoid persisting it into world-readable files
- make revocation and rotation straightforward
This proposal is about a better auth primitive, not about relaxing jackin’s security stance.
Implementation Outline
Section titled “Implementation Outline”At minimum, this design would touch:
src/config/roles.rs— auth mode resolution (resolve_auth_forward_mode,set_agent_auth_forward)src/config/mod.rs—AuthForwardModeenum, persistence typessrc/cli/config.rs—config authsubcommand help text and examplessrc/app/mod.rs—config authcommand dispatchsrc/instance/auth.rs— auth provisioning semantics for the new modesrc/runtime/launch.rs— runtime env injection and launch-time warnings- Agent authentication — explain concurrent-session tradeoffs and token mode
- config command — document setup and mode selection
- Configuration — config format updates
Follow-up Proposal
Section titled “Follow-up Proposal”The token-mode release leaves operator setup as a manual five-step copy-paste flow. A follow-up design proposal addresses that:
- Workspace Claude Token Setup —
guided
jackin workspace claude-token setuporchestrator that PTY-captures the upstreamclaude setup-tokenoutput, writes the result to 1Password (or a future keychain backend), wires the workspace config, and validates end-to-end. Also covers rotation, revocation, expiry banners, and TUI integration.
Open Questions
Section titled “Open Questions”- Where should jackin store a long-lived Claude token on macOS and Linux? (Addressed by the Workspace Claude Token Setup proposal: 1Password by default, OS keychain as a follow-up source.)
- Should token mode be an explicit opt-in only, or should it eventually replace
copyas the default recommendation? - Is a token-only mode sufficient, or should jackin also support an operator-provided
apiKeyHelperpath for terminal-only workflows? - Should jackin proactively detect likely-auth-drift conditions and recommend
purgeorsyncbefore the operator hits a 401?
Related Files
Section titled “Related Files”src/config/mod.rs—AuthForwardModeenum, persistence typessrc/config/roles.rs— auth mode resolutionsrc/cli/config.rs—jackin config authhelp textsrc/app/mod.rs— auth config command handlerssrc/instance/auth.rs— credential forwarding and state preparationsrc/runtime/launch.rs— launch-time env injection and notices- Agent authentication — current forwarding model and troubleshooting
- config command — current operator auth commands
- Configuration — persisted config schema