Skip to content

fix(ui): send token/password auth in Control UI insecure contexts#44865

Closed
asyncjason wants to merge 3 commits intoopenclaw:mainfrom
asyncjason:fix/control-ui-insecure-context-auth
Closed

fix(ui): send token/password auth in Control UI insecure contexts#44865
asyncjason wants to merge 3 commits intoopenclaw:mainfrom
asyncjason:fix/control-ui-insecure-context-auth

Conversation

@asyncjason
Copy link
Copy Markdown
Contributor

Summary

  • Problem: GatewayBrowserClient.sendConnect() only calls selectConnectAuth() inside if (isSecureContext), so when the Control UI is accessed over plain HTTP (where crypto.subtle is unavailable), the explicit token and password from the login form are never included in the connect frame.
  • Why it matters: The gateway sees no credentials, sharedAuthOk is false, and the connection is rejected with CONTROL_UI_DEVICE_IDENTITY_REQUIRED — even when dangerouslyDisableDeviceAuth is enabled. This makes the Control UI completely unusable over plain HTTP (common in LAN/Docker reverse-proxy setups).
  • What changed: After the isSecureContext block, the explicit token (this.opts.token) and password (this.opts.password) are now used as fallbacks when selectConnectAuth() was skipped. One new test verifies the insecure context path.
  • What did NOT change (scope boundary): Secure context behavior is unchanged — selectConnectAuth() still runs and its results take precedence via ?? fallback. Device identity flow, retry logic, and all existing auth paths are untouched.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Auth / tokens
  • UI / DX

Linked Issue/PR

User-visible / Behavior Changes

Control UI now connects successfully over plain HTTP when a valid token or password is provided. Previously it always failed with "device identity required".

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No — tokens were already available in this.opts, they just weren't being sent
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux (Debian 12, Docker)
  • Runtime/container: Docker (OpenClaw v2026.3.12)
  • Model/provider: N/A (gateway connection issue)
  • Integration/channel: Control UI WebSocket
  • Relevant config: gateway.controlUi.dangerouslyDisableDeviceAuth: true

Steps

  1. Run gateway on LAN host with dangerouslyDisableDeviceAuth: true
  2. Reverse-proxy behind plain HTTP (no TLS)
  3. Open Control UI via http://host/gateway/#token=<valid-token>

Expected

  • Control UI connects and shows dashboard

Actual (before fix)

  • Connection rejected: CONTROL_UI_DEVICE_IDENTITY_REQUIRED, close code 4008

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets

New test sends explicit token and password in insecure contexts without crypto.subtle fails before the fix (connect frame has auth: undefined) and passes after (connect frame includes token and password).

Gateway logs before fix: code=4008, reason="connect failed", detail CONTROL_UI_DEVICE_IDENTITY_REQUIRED
After fix: successful hello handshake, Control UI connected.

Human Verification (required)

  • Verified scenarios: Deployed to staging VM behind HTTP reverse proxy, Control UI connected successfully with token auth
  • Edge cases checked: Verified secure context (HTTPS/localhost) behavior unchanged — existing tests all pass
  • What you did not verify: Password-only auth in insecure context (tested token-only and token+password)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert the two-line change in sendConnect()
  • Files/config to restore: ui/src/ui/gateway.ts
  • Known bad symptoms reviewers should watch for: Auth regression in secure contexts (all existing tests cover this)

Risks and Mitigations

  • Risk: Explicit credentials could be sent when selectConnectAuth() already resolved different credentials
    • Mitigation: ?? operator ensures selectConnectAuth() results always take precedence; explicit values are only used when selectedAuth fields are undefined (i.e., when the function was skipped)

Authored by Claude Code (Opus 4.6). Reviewed and tested by @asyncjason.

Jason Separovic and others added 2 commits March 13, 2026 00:07
When crypto.subtle is unavailable (plain HTTP, non-localhost),
selectConnectAuth() is skipped entirely, leaving selectedAuth empty.
The explicit gateway token and password from the login form are read
into explicitGatewayToken but never used in the auth object sent in
the connect frame — auth ends up undefined.

Fall back to the explicit token/password when selectedAuth has no
auth fields, matching the comment on line 241 that promises
"fall back to token-only auth" in insecure contexts.

Without this fix, Control UI over plain HTTP always gets
"device identity required" even with dangerouslyDisableDeviceAuth
enabled, because sharedAuthOk is false (no token sent).
Verify that explicit token and password are sent in the connect
frame when crypto.subtle is unavailable (plain HTTP contexts).

Co-Authored-By: Claude <[email protected]>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR fixes a bug where GatewayBrowserClient.sendConnect() silently dropped explicit credentials when running in a plain-HTTP (insecure) context. Because selectConnectAuth() was only called inside the isSecureContext block, selectedAuth remained an empty object in insecure contexts, and the gateway received no auth — causing every connection attempt to be rejected.

The fix adds two ??-fallback assignments after the isSecureContext block. In insecure contexts (no crypto.subtle), selectedAuth is empty so the fallbacks resolve to the values from this.opts. In secure contexts, selectConnectAuth() already populates selectedAuth from the same source fields, making the fallbacks no-ops and preserving all existing behavior. A new test validates the insecure path by patching crypto.subtle to undefined, verifying the connect frame carries the supplied credentials and omits the device identity payload.

  • The ?? operator ensures selectConnectAuth() results take precedence in secure contexts
  • Device identity flow, retry logic, and all existing auth paths are untouched
  • Fully backward compatible — no config or migration required

Confidence Score: 5/5

  • This PR is safe to merge — the change is minimal, well-scoped, and the ?? fallbacks are provably no-ops in secure contexts.
  • The fix is two lines in sendConnect() and one new targeted test. In secure contexts, selectedAuth.authToken and selectedAuth.authPassword are derived from the exact same this.opts fields as the fallback values, making the ?? operators logically inert — no behavioral change on HTTPS/localhost. In insecure contexts the fallbacks correctly forward the supplied credentials that were previously being silently dropped. No auth paths, retry logic, or device identity flows are touched. All existing tests continue to pass, and the new test correctly exercises the bug path.
  • No files require special attention.

Last reviewed commit: b0f3077

@Niraven
Copy link
Copy Markdown

Niraven commented Mar 13, 2026

🔒 Automated Security Assessment

Status: ✅ PASSED — No security issues detected by automated scan.

Scan Date: 2026-03-13 09:12 UTC
Scanner: axis-main PR Security Scanner

Checks Performed

Check Result
Hardcoded secrets / credentials ✅ Pass
SQL injection risk (unprepared queries) ✅ Pass
Dangerous code execution (eval/exec/system) ✅ Pass
Sensitive file path exposure (.env, .key, .pem) ✅ Pass
New dependency additions ✅ Pass

Recommendation

Automated checks passed. Manual review is still recommended for:

  • Logic errors and edge cases
  • Architecture / data flow changes
  • Business logic validation

This comment was posted by the automated PR security scanner. False positives may occur — please verify findings manually.

@asyncjason
Copy link
Copy Markdown
Contributor Author

Superseded by #45088 which was merged with an equivalent fix (seeding selectedAuth with explicit credentials upfront rather than ?? fallback). Closing as duplicate.

@asyncjason asyncjason closed this Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Control UI fails to authenticate over plain HTTP (insecure context)

2 participants