OAuth Providers Tab
Adds a polished OAuth settings tab to the OpenClaw Control UI.
What It Installs
| File | Purpose |
|---|---|
ui/src/ui/views/ai-providers.ts | Lit HTML view — provider cards, mode tabs, OAuth spinner, manual-paste field |
ui/src/ui/controllers/ai-providers.ts | State management, RPC calls, provider catalogue |
src/gateway/server-methods/auth-login.ts | Gateway RPC handlers for all auth flows |
Plus wiring changes in:
ui/src/ui/app.ts— 6@state()propertiesui/src/ui/app-render.ts— render block forstate.tab === "ai-providers"ui/src/ui/app-settings.ts— tab load triggerui/src/ui/navigation.ts—Tabtype,TAB_PATHS,iconForTabsrc/gateway/server-methods.ts— registerauthLoginHandlerssrc/gateway/server-methods/plugins-ui.ts—BUILTIN_UI_VIEWSentryui/src/i18n/locales/en.ts—"ai-providers"label ("OAuth") and subtitle
Auth Flows
OpenAI — Codex PKCE OAuth
- Button opens the system browser via
openUrl() - Callback captured on
localhost:1455/auth/callback - Calls
loginOpenAICodex()from@mariozechner/pi-ai - Credentials written by
writeOAuthCredentials()+applyAuthProfileConfig() - WSL2 manual-paste fallback: On WSL2,
127.0.0.1:1455is unreachable from Windows. The UI shows a paste field where the user can paste the full redirect URL (http://localhost:1455/auth/callback?code=...&state=...). The gateway RPCauth.login.openai-codex.submit-coderesolves a deferred promise that races with the local callback server viaonManualCodeInput. - Session state:
aiProvidersOauthSessionIdset bystartOpenAICodexOAuth→ used byaiProvidersSubmitCodeto call the RPC
Anthropic — Subscription Token (setup-token)
- User runs
claude setup-tokenin terminal to generate ask-ant-oat01-...token - Pastes token into UI
- Validated by
validateAnthropicSetupToken(), stored viabuildTokenProfileId()+upsertAuthProfile() - Auto-detect button: Reads existing
accessTokenfrom~/.claude/.credentials.json(underclaudeAiOauth) and stores it viaauth.login.anthropic-autoRPC - ⚠️ Important: Anthropic has blocked some subscription usage outside Claude Code. The docs warn: "This credential is only authorized for use with Claude Code." Setup-token support is "technical compatibility only" with policy risk. If you get a "not authorized" error, an API key is required.
API Keys (all providers)
- Encrypted to
~/.openclaw/secrets.jsonvia existingsecrets.writeRPC - Env vars:
ANTHROPIC_API_KEY,OPENAI_API_KEY,GOOGLE_API_KEY,OPENROUTER_API_KEY
Gateway RPCs
| Method | Description |
|---|---|
auth.login.status | List all configured auth profiles |
auth.login.anthropic-token | Validate + store Anthropic setup-token |
auth.login.anthropic-auto | Auto-detect token from ~/.claude/.credentials.json |
auth.login.openai-codex | Run PKCE OAuth (opens browser) |
auth.login.openai-codex.submit-code | Manual paste of redirect URL (WSL2 fallback) |
auth.login.remove | Remove a profile by profileId |
Auth Badge System
The chat UI displays a badge next to assistant messages indicating which auth method was used.
Badge Rendering (grouped-render.ts)
| profileId pattern | Badge | CSS class |
|---|---|---|
__mode:oauth | OAuth (green) | auth-badge--oauth |
Contains :manual or claude-cli or starts with anthropic:oat | OAuth (green) | auth-badge--oauth |
Starts with anthropic: (catch-all) | API (blue) | auth-badge--api |
Starts with openai: | Fallback | auth-badge--fallback |
| Everything else | API | auth-badge--fallback |
The badge is determined by group.authProfileId in the message group. If the wrong profile is active, the wrong badge appears.
See references/auth-badge.ts.excerpt for the full function.
Auth Profile Order — Architecture & Troubleshooting
How Profile Selection Works
The gateway selects which auth profile to use via resolveAuthProfileOrder() in src/agents/auth-profiles/order.ts:
storedOrder (auth-profiles.json) → takes precedence
configuredOrder (openclaw.json) → fallback if no stored order
explicitProfiles (config keys) → fallback if no explicit order
storeProfiles (all in store) → last resort
Critical: auth-profiles.json order ALWAYS wins over openclaw.json order. If auth-profiles.json has a stale order array, the correct order in openclaw.json will never be consulted.
See references/auth-order.ts.excerpt for the key code.
Common Issue: Stale Order / Ghost Profiles
Symptom: Badge shows "API" or "Fallback" even though the subscription token is configured correctly.
Cause: auth-profiles.json has an order array referencing non-existent profiles (e.g. anthropic:manual from an old setup). The existing profile (like anthropic:default) gets selected instead of the subscription token profile.
Diagnosis:
# Check current auth status
openclaw models status
# Check what auth-profiles.json actually has
python3 -c "
import json
with open('$HOME/.openclaw/agents/main/agent/auth-profiles.json') as f:
d = json.load(f)
print('order:', d.get('order'))
print('lastGood:', d.get('lastGood'))
print('profiles:', list(d['profiles'].keys()))
"
Fix:
# Option 1: Remove stale order so openclaw.json becomes authoritative
python3 -c "
import json
path = '$HOME/.openclaw/agents/main/agent/auth-profiles.json'
with open(path) as f: d = json.load(f)
d.pop('order', None)
d.pop('lastGood', None)
with open(path, 'w') as f: json.dump(d, f, indent=2)
print('Stale order removed')
"
# Option 2: Set order via CLI (writes to auth-profiles.json)
openclaw models auth order set --provider anthropic anthropic:claude-cli anthropic:default
# Then restart gateway
(sleep 3 && systemctl --user restart openclaw-gateway) &
Why Option 1 is preferred: The CLI writes to auth-profiles.json, but gateway restarts or other processes can overwrite it. Removing the stored order lets openclaw.json (which is the canonical config) be the authority.
Repair Logic
resolveAuthProfileOrder() has a repair path (lines 96-100): if ALL profiles in the base order are missing from the store, it scans all stored profiles for the provider. But this only triggers when EVERY profile is missing — if even one exists (like anthropic:default), the repair doesn't kick in.
Config Verification
Ensure openclaw.json has the correct auth section:
{
"auth": {
"profiles": {
"anthropic:claude-cli": { "provider": "anthropic", "mode": "oauth" },
"anthropic:default": { "provider": "anthropic", "mode": "api_key" }
},
"order": {
"anthropic": ["anthropic:claude-cli", "anthropic:default"]
}
}
}
The mode field in openclaw.json profiles is checked against the type field in auth-profiles.json credentials. A mode: "oauth" config is compatible with both type: "oauth" and type: "token" credentials (special-cased in resolveAuthProfileEligibility).
Installation Steps
- Copy
references/view.ts→ui/src/ui/views/ai-providers.ts - Copy
references/controller.ts→ui/src/ui/controllers/ai-providers.ts - Copy
references/auth-login.ts→src/gateway/server-methods/auth-login.ts - Wire into the UI (see wiring changes above — follow patterns from other tabs like
apikeys) - Register
authLoginHandlersinsrc/gateway/server-methods.ts npm run build— zero new TS errors expected- Restart gateway:
(sleep 3 && systemctl --user restart openclaw-gateway) & - Open Settings → OAuth in the Control UI
Provider Catalogue
Defined in PROVIDER_CATALOGUE in references/controller.ts. To add a provider:
{
id: "mistral",
name: "Mistral AI",
logo: "🌊",
color: "#ff7000",
description: "Mistral Large, Codestral — fast European models",
connectOptions: [
{ mode: "api-key", label: "API Key", hint: "Get a key from console.mistral.ai", profileMode: "api_key" },
],
}
Known Gotchas
- Auth order precedence:
auth-profiles.jsonorder beatsopenclaw.json. If badge shows wrong auth mode, check for stale order in auth-profiles.json first. - Gateway restart overwrites: Manual edits to
auth-profiles.jsoncan be overwritten by gateway processes. Prefer editingopenclaw.jsonfor persistent config. - WSL2 loopback isolation: The OpenAI Codex OAuth callback server binds to
127.0.0.1:1455(hardcoded in@mariozechner/pi-ai). Windows browsers can't reach WSL2 localhost. Use the manual-paste field. claude setup-tokenrequires interactive TTY: Uses Ink/raw mode — cannot be run non-interactively. The auto-detect button reads~/.claude/.credentials.jsonas a workaround.- Anthropic policy risk:
sk-ant-oat01-*tokens may be blocked outside Claude Code. If API calls return authorization errors, switch to an API key. lastGoodpersistence: ThelastGoodfield inauth-profiles.jsoncan cause the gateway to skip the configured order and jump straight to a previously-working profile. Remove it along withorderwhen troubleshooting.
Source Files
- references/controller.ts — full controller with PROVIDER_CATALOGUE and all action handlers
- references/view.ts — full Lit HTML view with manual-paste field
- references/auth-login.ts — full gateway RPC handlers including WSL2 fallback + auto-detect
- references/auth-order.ts.excerpt — auth profile order resolution logic (key snippet)
- references/auth-badge.ts.excerpt — badge rendering function (key snippet)
Changelog
v1.1.0
- Added WSL2 manual-paste fallback for OpenAI Codex OAuth (
onManualCodeInput+auth.login.openai-codex.submit-codeRPC) - Added Anthropic auto-detect button (
auth.login.anthropic-autoRPC) — reads from~/.claude/.credentials.json - Added auth badge rendering reference (
renderAuthBadge()fromgrouped-render.ts) - Added auth profile order architecture documentation with troubleshooting guide
- Added stale order/ghost profile diagnosis and fix procedures
- Documented
resolveAuthProfileOrder()precedence: stored order > config order > explicit profiles > store profiles - Added known gotchas section covering order precedence, gateway overwrites, WSL2 loopback, TTY requirements, and Anthropic policy risk
v1.0.0
- Initial release with Anthropic setup-token, OpenAI Codex PKCE OAuth, and API key flows