# Credential System v2 (API + OAuth/token) — Implementation Checklist

**Date:** 2026-02-15

**Parent roadmap:** `docs/plans/2026-02-15-openclaw-gap-roadmap.md`

**Goal:** Close the gap item "OAuth subscription auth" by supporting **both** API-key credentials and OAuth/token-based credentials (provider-specific) with consistent UX, per-tier control, and deterministic resolution.

## Scope Summary

- Add `auth_mode` **per tier** (default/fast/complex/local and any `local_providers` entries).
- Keep backward compatibility with existing `use_oauth` behavior.
- Add stored credential support where it currently doesn't exist:
  - OpenAI: stored API key (OAuth already exists)
  - Anthropic: stored auth token (API key already exists)
- Improve doctor output to surface which auth sources are present (without revealing secrets).

Non-goals (explicitly out of scope for this checklist):

- Inventing an Anthropic OAuth device flow.
- Building new provider integrations (Vercel/MiniMax/etc.).

## Current Baseline (important constraints)

- OpenAI OAuth uses the ChatGPT/Codex backend endpoint (SSE) and currently **does not support tools** on that path.
  - Source: `src/models/openai.ts`
- Anthropic supports `apiKey` and `authToken` in `AnthropicClientConfig`.
  - Source: `src/models/anthropic.ts`
- Stored credentials live at `~/.config/flynn/auth.json`.
  - Source: `src/auth/openai.ts`, `src/auth/anthropic.ts`

## Design Decisions

### 1) New config field: `auth_mode`

Add `auth_mode` to the per-tier model config:

- `auto` (default)
- `api_key`
- `oauth`

`oauth` is interpreted as "OAuth/token mode" (provider-specific). For Anthropic, that means `auth_token`.

### 2) Backward compatibility: `use_oauth`

Preserve `use_oauth` as a compatibility alias.

Recommended rule:

- If `auth_mode` is set: it wins.
- Else if `use_oauth: true`: treat as `auth_mode: oauth`.

### 3) Credential resolution order

For each provider, resolve the required credential type by trying:

1) config (`api_key` / `auth_token`)
2) env var
3) auth store (`~/.config/flynn/auth.json`)

`auth_mode` controls which credential type is required.

## PR Breakdown (atomic, test-backed)

### PR 1 — Schema + docs: per-tier `auth_mode`

Checklist:

- [ ] Add `auth_mode` to `modelConfigBaseSchema` in `src/config/schema.ts`.
- [ ] Update `src/config/schema.test.ts` to cover defaults + validation.
- [ ] Update `README.md` config examples (brief mention).
- [ ] Update `config/default.yaml` comment/help text (brief mention).

Acceptance:

- Config parses with no changes (defaults preserved).
- Setting `auth_mode: oauth` or `auth_mode: api_key` validates.

Tests:

- `pnpm test:run src/config/schema.test.ts`

---

### PR 2 — OpenAI auth store: add API-key storage

Goal: allow OpenAI to run without `api_key` in YAML.

Checklist:

- [ ] Extend `src/auth/openai.ts` `AuthStore` shape to allow `openai.api_key` alongside existing OAuth info.
- [ ] Add functions:
  - [ ] `loadStoredOpenAIApiKey()`
  - [ ] `storeOpenAIApiKey(key)`
  - [ ] `clearOpenAIApiKey()`
  - [ ] `getOpenAIApiKey()` (env override + store)
- [ ] Keep existing OAuth store code working unchanged.
- [ ] Add/extend tests for new store functions.

Files:

- `src/auth/openai.ts`
- `src/auth/openai.test.ts` (or add if missing)

Acceptance:

- Stored OpenAI API key is written to `~/.config/flynn/auth.json` with `0600` permissions.
- OAuth entry remains backward compatible.

Tests:

- `pnpm test:run src/auth/openai.test.ts`

---

### PR 3 — Anthropic auth store: add auth-token storage

Goal: allow `auth_token` to be stored and selected with `auth_mode: oauth`.

Checklist:

- [ ] Extend `src/auth/anthropic.ts` auth store shape to include `auth_token`.
- [ ] Add functions:
  - [ ] `loadStoredAnthropicAuthToken()`
  - [ ] `storeAnthropicAuthToken(token)`
  - [ ] `clearAnthropicAuthToken()`
  - [ ] `getAnthropicAuthToken()`
- [ ] Extend `src/auth/anthropic.test.ts`.

Files:

- `src/auth/anthropic.ts`
- `src/auth/anthropic.test.ts`

Acceptance:

- `auth_token` can be stored and resolved without being present in YAML.

Tests:

- `pnpm test:run src/auth/anthropic.test.ts`

---

### PR 4 — CLI commands for managing new stored credentials

Checklist:

- [ ] Add `flynn openai-key` command (store API key in auth.json).
- [ ] Extend `flynn anthropic-auth` to support storing either API key or auth token:
  - [ ] recommended: `flynn anthropic-auth --token` OR `flynn anthropic-token`
- [ ] Update `src/cli/index.ts` registration.

Files:

- `src/cli/openai-key.ts` (new)
- `src/cli/anthropic-auth.ts` (modify)
- `src/cli/index.ts`

Acceptance:

- CLI can store credentials without printing them.
- Re-running commands detects existing stored credentials and exits cleanly.

Tests:

- Add targeted unit tests if the CLI layer has existing patterns; otherwise validate via integration tests where feasible.

---

### PR 5 — TUI `/login` UX: OpenAI choice (OAuth vs API key) + Anthropic token

Checklist:

- [ ] Update `/login openai` in `src/frontends/tui/minimal.ts`:
  - [ ] Present a simple prompt: "1) OAuth device flow  2) Paste API key"
  - [ ] Store selected credential via auth store
- [ ] Add `/login anthropic` in `src/frontends/tui/minimal.ts`:
  - [ ] "1) Paste API key  2) Paste auth token"
- [ ] Keep existing `/login github` and `/login zai` behavior intact.

Files:

- `src/frontends/tui/minimal.ts`
- `src/frontends/tui/commands.ts` (if command parsing needs to accept new provider)

Acceptance:

- TUI can store OpenAI API key or OAuth token.
- TUI can store Anthropic API key or auth token.

Tests:

- Add or extend minimal TUI tests as needed (existing suite patterns exist for model switching).

---

### PR 6 — Model factory: enforce `auth_mode` per tier

This is the core runtime change.

Checklist:

- [ ] Update `src/daemon/models.ts`:
  - [ ] Read `cfg.auth_mode` (or inferred from `use_oauth`) per tier.
  - [ ] For OpenAI:
    - [ ] `auth_mode=oauth`: configure `OpenAIClient({ useOAuth: true })` and verify OAuth tokens exist.
    - [ ] `auth_mode=api_key`: configure `OpenAIClient({ apiKey: resolvedKey })`.
  - [ ] For Anthropic:
    - [ ] `auth_mode=oauth`: require auth token (config/env/store).
    - [ ] `auth_mode=api_key`: require API key (config/env/store).
  - [ ] For other providers:
    - [ ] define behavior explicitly (likely `api_key` only unless provider already supports token-style auth).
  - [ ] Ensure error messages name the expected auth type and remediation.

Files:

- `src/daemon/models.ts`
- potentially `src/models/openai.ts` (if you decide to unify API key vs OAuth selection naming)

Tests:

- `src/daemon/clientFactory.test.ts`
  - [ ] auth_mode precedence over use_oauth
  - [ ] auto -> api key path
  - [ ] oauth -> token path
  - [ ] correct failures when missing

Acceptance:

- Selecting `auth_mode` changes runtime behavior deterministically.

---

### PR 7 — Doctor: report auth source availability

Checklist:

- [ ] Extend `checkModelConnectivity` in `src/cli/doctor.ts` to reflect `auth_mode`:
  - [ ] If `auth_mode=api_key`, warn/fail when API key is absent from config/env/store.
  - [ ] If `auth_mode=oauth`, warn/fail when OAuth/token is absent.
  - [ ] If `auth_mode=auto`, keep current behavior but improve messaging.
- [ ] Add tests in `src/cli/doctor.test.ts`.

Acceptance:

- Doctor output tells user what to do next (command name + env var) without exposing secrets.

## Final Integration Checks

- [ ] `pnpm typecheck`
- [ ] `pnpm test:run`
- [ ] Update `docs/plans/state.json` entry for this checklist once implemented (status, summary, test status).
