ResearchAuthentication

Reliable Claude Authentication Strategy

Status: Deferred — needs design work

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.json is 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 Capsule-managed 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

  • jackin 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 /login prompts are especially painful inside isolated runtimes because browser-driven recovery breaks flow

Current State

Status as of the Token-mode release:

  • sync — overwrite container auth from host on each launch when host auth exists (default since the sync-default release)
  • ignore — never forward host auth; require in-container /login
  • token — inject CLAUDE_CODE_OAUTH_TOKEN from 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_TOKEN from claude 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.

Goal

Keep the current convenience goal:

  1. If the operator is already authenticated locally, jackin should be able to start an agent without requiring manual /login inside the container.
  2. The chosen auth mechanism should remain reliable across restarts and normal day-to-day usage.
  3. The operator should understand which mode they are using and what tradeoffs it carries.
  4. Multiple active jackin sessions should degrade gracefully rather than surprising the operator with intermittent 401s.

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

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

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

Claude Code documents a supported one-year OAuth token flow via:

claude setup-token

The 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

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 copy is selected for a pre-existing container family
  • surface a targeted recovery hint when a container hits 401 (switch to sync, purge, or use 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

Delivered (three-PR series):

  1. sync is the default auth-forwarding mode.
  2. A workspace-scoped env resolver populates CLAUDE_CODE_OAUTH_TOKEN from op:// references or host env vars.
  3. token mode uses that resolved env instead of bind-mounting /login session state. Token mode is now the recommended choice for long-lived or concurrent jackin sessions; sync remains a good default for ad-hoc single-session usage.

Suggested Operator UX

CLI

Examples:

jackin config auth set sync
jackin config auth set ignore
jackin config auth set token

Potential setup helper:

jackin config auth setup-token

That command could either:

  • guide the operator through claude setup-token
  • or simply explain how to generate and register the token manually

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

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 token
  • claude auth: none (manual /login required)

If multiple containers are active with copied session auth, jackin should say so directly.

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 security stance.

Implementation Outline

At minimum, this design would touch:

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 setup orchestrator that PTY-captures the upstream claude setup-token output, 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

  1. 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.)
  2. Should token mode be an explicit opt-in only, or should it eventually replace copy as the default recommendation?
  3. Is a token-only mode sufficient, or should jackin also support an operator-provided apiKeyHelper path for terminal-only workflows?
  4. Should jackin proactively detect likely-auth-drift conditions and recommend purge or sync before the operator hits a 401?
  • Auth reliability and convenience program — umbrella program placing this item's design history in context; the concurrent-session concerns documented here are addressed across Phases 1–4 of that program.

On this page