Adds a polished OAuth settings tab to the OpenClaw Control UI.
| 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 for state.tab === "ai-providers"ui/src/ui/app-settings.ts — tab load triggerui/src/ui/navigation.ts — Tab type, TAB_PATHS, iconForTabsrc/gateway/server-methods.ts — register authLoginHandlerssrc/gateway/server-methods/plugins-ui.ts — BUILTIN_UI_VIEWS entryui/src/i18n/locales/en.ts — "ai-providers" label ("OAuth") and subtitleopenUrl()localhost:1455/auth/callbackloginOpenAICodex() from @mariozechner/pi-aiwriteOAuthCredentials() + applyAuthProfileConfig()127.0.0.1:1455 is 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 RPC auth.login.openai-codex.submit-code resolves a deferred promise that races with the local callback server via onManualCodeInput.aiProvidersOauthSessionId set by startOpenAICodexOAuth → used by aiProvidersSubmitCode to call the RPCclaude setup-token in terminal to generate a sk-ant-oat01-... tokenvalidateAnthropicSetupToken(), stored via buildTokenProfileId() + upsertAuthProfile()accessToken from ~/.claude/.credentials.json (under claudeAiOauth) and stores it via auth.login.anthropic-auto RPC~/.openclaw/secrets.json via existing secrets.write RPCANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, OPENROUTER_API_KEY| 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 |
The chat UI displays a badge next to assistant messages indicating which auth method was used.
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.
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.
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.
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.
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).
references/view.ts → ui/src/ui/views/ai-providers.tsreferences/controller.ts → ui/src/ui/controllers/ai-providers.tsreferences/auth-login.ts → src/gateway/server-methods/auth-login.tsapikeys)authLoginHandlers in src/gateway/server-methods.tsnpm run build — zero new TS errors expected(sleep 3 && systemctl --user restart openclaw-gateway) &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" },
],
}
auth-profiles.json order beats openclaw.json. If badge shows wrong auth mode, check for stale order in auth-profiles.json first.auth-profiles.json can be overwritten by gateway processes. Prefer editing openclaw.json for persistent config.127.0.0.1:1455 (hardcoded in @mariozechner/pi-ai). Windows browsers can't reach WSL2 localhost. Use the manual-paste field.claude setup-token requires interactive TTY: Uses Ink/raw mode — cannot be run non-interactively. The auto-detect button reads ~/.claude/.credentials.json as a workaround.sk-ant-oat01-* tokens may be blocked outside Claude Code. If API calls return authorization errors, switch to an API key.lastGood persistence: The lastGood field in auth-profiles.json can cause the gateway to skip the configured order and jump straight to a previously-working profile. Remove it along with order when troubleshooting.onManualCodeInput + auth.login.openai-codex.submit-code RPC)auth.login.anthropic-auto RPC) — reads from ~/.claude/.credentials.jsonrenderAuthBadge() from grouped-render.ts)resolveAuthProfileOrder() precedence: stored order > config order > explicit profiles > store profiles共 1 个版本