# Reliable Claude Authentication Strategy (https://jackin.tailrocks.com/reference/research/authentication/claude-auth-strategy/)



**Status**: Deferred — needs design work

## Problem [#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:

```text
Please run /login · API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"Invalid authentication credentials"}}
```

## Why It Matters [#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 [#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 [#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 [#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 [#design-options]

### Option 1: Keep `copy` as the primary workflow [#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 [#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` [#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:

```bash
claude setup-token
```

The output can be set as:

```bash
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 [#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 [#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 [#suggested-operator-ux]

### CLI [#cli]

Examples:

```bash
jackin config auth set sync
jackin config auth set ignore
jackin config auth set token
```

Potential setup helper:

```bash
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 [#config]

Suggested shape:

```toml
[claude]
auth_forward = "token"
```

If token mode needs an explicit token source, keep it operator-owned rather than agent-owned.

Possible configuration directions:

```toml
[claude]
auth_forward = "token"
oauth_token_source = "keychain"
```

or:

```toml
[claude]
auth_forward = "token"
oauth_token_env = "JACKIN_CLAUDE_OAUTH_TOKEN"
```

The exact storage contract needs a separate security pass.

### Runtime messaging [#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 [#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 [#implementation-outline]

At minimum, this design would touch:

* <RepoFile path="crates/jackin-config/src/app_config/roles.rs">crates/jackin-config/src/app\_config/roles.rs</RepoFile> — auth mode resolution (`resolve_auth_forward_mode`, `set_agent_auth_forward`)
* <RepoFile path="crates/jackin-config/src/app_config.rs">crates/jackin-config/src/app\_config.rs</RepoFile> — `AuthForwardMode` enum, persistence types
* <RepoFile path="crates/jackin/src/cli/config.rs">crates/jackin/src/cli/config.rs</RepoFile> — `config auth` subcommand help text and examples
* <RepoFile path="crates/jackin/src/app.rs">crates/jackin/src/app.rs</RepoFile> — `config auth` command dispatch
* <RepoFile path="crates/jackin-instance/src/auth.rs">crates/jackin-instance/src/auth.rs</RepoFile> — auth provisioning semantics for the new mode
* <RepoFile path="crates/jackin-runtime/src/runtime/launch.rs">crates/jackin-runtime/src/runtime/launch.rs</RepoFile> — runtime env injection and launch-time warnings
* [Agent authentication](/guides/authentication/agents/) — explain concurrent-session tradeoffs and token mode
* [config command](/commands/config/) — document setup and mode selection
* [Configuration](/reference/runtime/configuration/) — config format updates

## Follow-up Proposal [#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](/roadmap/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 [#open-questions]

1. Where should jackin store a long-lived Claude token on macOS and Linux?
   &#x2A;(Addressed by the [Workspace Claude Token Setup](/roadmap/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?

## Related work [#related-work]

* [Auth reliability and convenience program](/roadmap/auth-reliability-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.

## Related Files [#related-files]

* <RepoFile path="crates/jackin-config/src/app_config.rs">crates/jackin-config/src/app\_config.rs</RepoFile> — `AuthForwardMode` enum, persistence types
* <RepoFile path="crates/jackin-config/src/app_config/roles.rs">crates/jackin-config/src/app\_config/roles.rs</RepoFile> — auth mode resolution
* <RepoFile path="crates/jackin/src/cli/config.rs">crates/jackin/src/cli/config.rs</RepoFile> — `jackin config auth` help text
* <RepoFile path="crates/jackin/src/app.rs">crates/jackin/src/app.rs</RepoFile> — auth config command handlers
* <RepoFile path="crates/jackin-instance/src/auth.rs">crates/jackin-instance/src/auth.rs</RepoFile> — credential forwarding and state preparation
* <RepoFile path="crates/jackin-runtime/src/runtime/launch.rs">crates/jackin-runtime/src/runtime/launch.rs</RepoFile> — launch-time env injection and notices
* [Agent authentication](/guides/authentication/agents/) — current forwarding model and troubleshooting
* [config command](/commands/config/) — current operator auth commands
* [Configuration](/reference/runtime/configuration/) — persisted config schema
