Skip to content

fix(ui): keep shared auth on insecure control-ui connects#45088

Merged
velvet-shark merged 1 commit intomainfrom
codex/fix-control-ui-insecure-shared-auth
Mar 13, 2026
Merged

fix(ui): keep shared auth on insecure control-ui connects#45088
velvet-shark merged 1 commit intomainfrom
codex/fix-control-ui-insecure-shared-auth

Conversation

@velvet-shark
Copy link
Copy Markdown
Member

Summary

  • Problem: plain-HTTP Control UI sessions behind LAN and reverse-proxy setups were dropping explicit shared token/password auth before the first WebSocket connect frame, so the gateway rejected them with device identity required.
  • Why it matters: this broke the intended allowInsecureAuth + dangerouslyDisableDeviceAuth token flow for self-hosted HTTP deployments even though same-tab refresh/navigation had already been fixed separately.
  • What changed: initialize Control UI connect auth from the explicit shared token/password before the secure-context device-auth branch, and add regression tests for insecure token/password connects.
  • What did NOT change (scope boundary): cached device-token fallback is still limited to the existing trusted retry path, and this PR does not change the #40892 sessionStorage same-tab behavior or add any cross-tab token sharing.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • Control UI now preserves explicit shared token auth on insecure http:// first-connect flows instead of dropping auth before the first WebSocket handshake.
  • Control UI now preserves explicit shared password auth on the same insecure first-connect path.
  • Cached device tokens are still not sent on the insecure first connect.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (Yes)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

This restores the explicit token/password that the operator already supplied to the existing first-connect frame on insecure Control UI sessions. It does not broaden cached device-token fallback, does not add storage, and keeps the trusted retry boundary unchanged.

Repro + Verification

Environment

  • OS: macOS 15.7.4 (arm64)
  • Runtime/container: Control UI Vitest projects + local runtime repro script
  • Model/provider: N/A
  • Integration/channel (if any): Control UI
  • Relevant config (redacted): gateway.auth.mode: "token", gateway.controlUi.allowInsecureAuth: true, gateway.controlUi.dangerouslyDisableDeviceAuth: true

Steps

  1. Open the Control UI over plain HTTP with an explicit shared token or password configured.
  2. Let the browser receive the WebSocket connect.challenge event.
  3. Inspect the first connect request payload.

Expected

  • The first connect frame includes the explicit shared token or password.
  • No cached device token is sent on that insecure first connect.

Actual

  • Before this fix, insecure contexts built the first connect frame from an empty auth selection state, so params.auth was omitted entirely.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • insecure http:// connect with explicit shared token now sends auth.token
    • insecure http:// connect with explicit shared password now sends auth.password
    • existing trusted one-shot device-token retry tests still pass unchanged
    • existing same-tab navigation/refresh browser tests from #40892 still pass
  • Edge cases checked:
    • insecure first-connect still omits cached deviceToken
    • secure-context explicit shared auth still signs with the shared token rather than the cached device token
  • What you did not verify:
    • a manual live repro against a remote VPS/reverse-proxy deployment

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert commit 60abd798cffa99a9f03ac9936c47e8dc3f87deca
  • Files/config to restore: ui/src/ui/gateway.ts and ui/src/ui/gateway.node.test.ts
  • Known bad symptoms reviewers should watch for: insecure HTTP Control UI connects still fail with device identity required, or cached device tokens start appearing on the first insecure connect

Risks and Mitigations

  • Risk: restoring explicit shared auth on insecure contexts could accidentally reintroduce cached device-token fallback on the same path.
    • Mitigation: the new tests assert token/password presence while also asserting deviceToken stays absent.

@openclaw-barnacle openclaw-barnacle bot added the app: web-ui App: web-ui label Mar 13, 2026
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 13, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟠 High Control UI sends shared token/password over potentially plaintext WebSocket in insecure contexts

1. 🟠 Control UI sends shared token/password over potentially plaintext WebSocket in insecure contexts

Property Value
Severity High
CWE CWE-319
Location ui/src/ui/gateway.ts:240-275

Description

In GatewayBrowserClient.sendConnect(), the client now includes opts.token / opts.password in the connect request even when running in a non-secure browser context (where crypto.subtle is unavailable).

  • isSecureContext is determined only via crypto.subtle availability, not by checking whether the WebSocket URL uses wss://.
  • When the configured gateway URL is ws://..., the connect frame is sent via WebSocket.send(...) in cleartext, exposing the shared token/password to passive network observers / MitM (LAN, Wi‑Fi, misconfigured reverse proxy paths).
  • Prior behavior avoided sending explicit shared auth in insecure contexts (selectedAuth started empty), reducing credential exposure before the first handshake.

Vulnerable flow:

  • Input: this.opts.token / this.opts.password
  • Auth frame creation: auth = { token, password, ... }
  • Sink: this.ws.send(JSON.stringify(frame)) over ws:// when configured

Recommendation

Prevent shared credentials from being sent over plaintext transport.

Suggested hardening (client-side):

  • Parse this.opts.url and block sending token/password when the WebSocket protocol is ws: unless the target is loopback.
  • Preferably require wss: whenever token/password is set.
  • If insecure auth must be supported for localhost, gate it explicitly (e.g., allowInsecureCredentialSend: true) and show a prominent UI warning.

Example:

const gatewayUrl = new URL(this.opts.url, window.location.href);
const isWsPlaintext = gatewayUrl.protocol === "ws:";
const isLoopback = ["localhost", "127.0.0.1", "::1"].includes(gatewayUrl.hostname);

const explicitGatewayToken = this.opts.token?.trim() || undefined;
const explicitPassword = this.opts.password?.trim() || undefined;

if (isWsPlaintext && !isLoopback && (explicitGatewayToken || explicitPassword)) {
  throw new Error("Refusing to send credentials over ws://; use wss://");
}

Server-side guidance (docs/config):

  • Recommend wss:// (TLS) for any non-loopback Control UI usage; avoid enabling insecure auth except for true localhost scenarios.

Analyzed PR: #45088 at commit 99eb3fd

Last updated on: 2026-03-13T13:43:24Z

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR fixes a bug where plain-HTTP (insecure context) Control UI sessions dropped explicit shared token/password auth before the first WebSocket connect frame, causing the gateway to reject connections with "device identity required."

Root cause: selectedAuth was initialized as { canFallbackToShared: false } with no authToken/authPassword. On insecure contexts, the if (isSecureContext) branch was skipped entirely, so selectedAuth.authToken stayed undefined and the auth parameter was omitted from the connect frame.

Fix: Pre-populate selectedAuth with the explicit token/password before the isSecureContext branch. In secure contexts, selectConnectAuth() still fully overwrites selectedAuth (including re-reading this.opts.token and this.opts.password), so there is no behavioral regression on HTTPS/localhost paths.

Key points:

  • The fix is minimal and surgical — only 6 lines changed in gateway.ts
  • New tests correctly verify token and password paths on insecure connect, and assert that device-identity operations (loadOrCreateDeviceIdentity, signDevicePayload) are not invoked
  • The stubInsecureCrypto helper correctly simulates an insecure context by removing crypto.subtle while keeping crypto.randomUUID for request ID generation
  • The device token retry boundary is unchanged — Boolean(deviceIdentity) in the catch block's shouldRetryWithDeviceToken check remains false on insecure contexts

Confidence Score: 5/5

  • This PR is safe to merge — the change is minimal, well-tested, and does not expand the cached device-token fallback path.
  • The fix is a single-method, 6-line change with clear before/after behavior. The secure context path is unaffected because selectConnectAuth() fully overwrites selectedAuth. Two targeted regression tests cover the token and password cases, and each asserts that loadOrCreateDeviceIdentity and signDevicePayload are not called. Existing tests continue to cover the secure context and device token retry paths.
  • No files require special attention.

Last reviewed commit: e5fbf3d

@velvet-shark velvet-shark self-assigned this Mar 13, 2026
@velvet-shark velvet-shark force-pushed the codex/fix-control-ui-insecure-shared-auth branch 3 times, most recently from 6a34bf0 to e27b2dd Compare March 13, 2026 13:20
@velvet-shark velvet-shark force-pushed the codex/fix-control-ui-insecure-shared-auth branch from e27b2dd to 99eb3fd Compare March 13, 2026 13:25
@velvet-shark velvet-shark merged commit 0a3b9a9 into main Mar 13, 2026
10 checks passed
@velvet-shark velvet-shark deleted the codex/fix-control-ui-insecure-shared-auth branch March 13, 2026 13:25
@velvet-shark
Copy link
Copy Markdown
Member Author

Merged via squash.

Thanks @velvet-shark!

mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 13, 2026
* main: (168 commits)
  fix: stabilize macos daemon onboarding
  fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088)
  docs(plugins): clarify workspace shadowing
  fix(node-host): harden perl approval binding
  fix(node-host): harden pnpm approval binding
  fix(discovery): add missing domain to wideArea Zod config schema (openclaw#35615)
  chore(gitignore): add docker-compose override (openclaw#42879)
  feat(ios): add onboarding welcome pager (openclaw#45054)
  fix(signal): add groups config to Signal channel schema (openclaw#27199)
  fix: restore web fetch firecrawl config in runtime zod schema (openclaw#42583)
  fix: polish Android QR scanner onboarding (openclaw#45021)
  fix(android): use Google Code Scanner for onboarding QR
  fix(config): add missing params field to agents.list[] validation schema (openclaw#41171)
  docs(contributing): update Android app ownership
  fix(agents): rephrase session reset prompt to avoid Azure content filter (openclaw#43403)
  test(config): cover requiresOpenAiAnthropicToolPayload in compat schema fixture
  fix(agents): respect explicit user compat overrides for non-native openai-completions (openclaw#44432)
  Android: fix HttpURLConnection leak in TalkModeVoiceResolver (openclaw#43780)
  Docker: add OPENCLAW_TZ timezone support (openclaw#34119)
  fix(agents): avoid injecting memory file twice on case-insensitive mounts (openclaw#26054)
  ...
Arry8 added a commit to Arry8/openclaw that referenced this pull request Mar 13, 2026
* fix(node-host): harden pnpm approval binding

* fix(node-host): harden perl approval binding

* docs(plugins): clarify workspace shadowing

* fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* docs: add Claude Code token efficiency playbook

* docs: compress playbook for agent consumption

* docs: restructure playbook for long-context patterns

* pre-commit: add incremental repo-map updater

Auto-updates docs/repo-map.json exports and dependencies when
src/ or extensions/ .ts files are committed. Preserves existing
purpose fields, removes deleted files, adds new files with empty
purpose. Regex-based extraction, no dependencies.

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Radek Sienkiewicz <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
z-hao-wang pushed a commit to z-hao-wang/openclaw that referenced this pull request Mar 13, 2026
…5088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
frankekn pushed a commit to xinhuagu/openclaw that referenced this pull request Mar 14, 2026
…5088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
hougangdev pushed a commit to hougangdev/clawdbot that referenced this pull request Mar 14, 2026
…5088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
ecochran76 pushed a commit to ecochran76/openclaw that referenced this pull request Mar 14, 2026
…5088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
Arry8 added a commit to Arry8/openclaw that referenced this pull request Mar 15, 2026
* fix(node-host): harden pnpm approval binding

* fix(node-host): harden perl approval binding

* docs(plugins): clarify workspace shadowing

* fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* docs: add Claude Code token efficiency playbook

* docs: compress playbook for agent consumption

* docs: restructure playbook for long-context patterns

* pre-commit: add incremental repo-map updater

Auto-updates docs/repo-map.json exports and dependencies when
src/ or extensions/ .ts files are committed. Preserves existing
purpose fields, removes deleted files, adds new files with empty
purpose. Regex-based extraction, no dependencies.

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Radek Sienkiewicz <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Interstellar-code pushed a commit to Interstellar-code/operator1 that referenced this pull request Mar 16, 2026
…5088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
Arry8 added a commit to Arry8/openclaw that referenced this pull request Mar 16, 2026
* fix(node-host): harden pnpm approval binding

* fix(node-host): harden perl approval binding

* docs(plugins): clarify workspace shadowing

* fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* docs: add Claude Code token efficiency playbook

* docs: compress playbook for agent consumption

* docs: restructure playbook for long-context patterns

* pre-commit: add incremental repo-map updater

Auto-updates docs/repo-map.json exports and dependencies when
src/ or extensions/ .ts files are committed. Preserves existing
purpose fields, removes deleted files, adds new files with empty
purpose. Regex-based extraction, no dependencies.

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Radek Sienkiewicz <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Arry8 added a commit to Arry8/openclaw that referenced this pull request Mar 23, 2026
* fix(node-host): harden pnpm approval binding

* fix(node-host): harden perl approval binding

* docs(plugins): clarify workspace shadowing

* fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* docs: add Claude Code token efficiency playbook

* docs: compress playbook for agent consumption

* docs: restructure playbook for long-context patterns

* pre-commit: add incremental repo-map updater

Auto-updates docs/repo-map.json exports and dependencies when
src/ or extensions/ .ts files are committed. Preserves existing
purpose fields, removes deleted files, adds new files with empty
purpose. Regex-based extraction, no dependencies.

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Radek Sienkiewicz <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
sbezludny pushed a commit to sbezludny/openclaw that referenced this pull request Mar 27, 2026
…5088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark
Arry8 added a commit to Arry8/openclaw that referenced this pull request Mar 29, 2026
* fix(node-host): harden pnpm approval binding

* fix(node-host): harden perl approval binding

* docs(plugins): clarify workspace shadowing

* fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088)

Merged via squash.

Prepared head SHA: 99eb3fd
Co-authored-by: velvet-shark <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Reviewed-by: @velvet-shark

* docs: add Claude Code token efficiency playbook

* docs: compress playbook for agent consumption

* docs: restructure playbook for long-context patterns

* pre-commit: add incremental repo-map updater

Auto-updates docs/repo-map.json exports and dependencies when
src/ or extensions/ .ts files are committed. Preserves existing
purpose fields, removes deleted files, adds new files with empty
purpose. Regex-based extraction, no dependencies.

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Radek Sienkiewicz <[email protected]>
Co-authored-by: velvet-shark <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui maintainer Maintainer-authored PR size: S

Projects

None yet

1 participant