Skip to content

fix(gateway,companion): role-gate /ws/live and check role before Companion connects - #257

Merged
Telli merged 4 commits into
mainfrom
fix/ws-live-role-and-companion-role-hint
Sep 29, 2026
Merged

Telli merged 4 commits into
mainfrom
fix/ws-live-role-and-companion-role-hint

Conversation

@Telli

@Telli Telli commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Description

Follow-up to #256. This branch also carries #261's commit (perf(gateway): verify each request's account token once); merge #261 first and it drops out of this diff.

Two gaps left after #256:

  1. /ws/live admitted any authenticated role. The live model bridge runs no tools, but it opens a live session on the gateway's provider credentials, so a viewer token could spend them.
  2. Companion opened the chat socket before learning the account's role. A viewer connected, was closed with 1008, and only then saw why, via the OnClosed handling from fix(gateway): role-gate agent execution and close /apps loopback bypass #256.

Summary

  • /ws/live uses EndpointHelpers.CanExecuteAgent like /ws. Below operator it is accepted, then closed with 1008 and the operator-role reason. AllowViewerAgentExecution and the denial log cover it like the other surfaces.
  • Companion loads /auth/session before connecting. When the gateway reports a role below operator, Companion says so ("Chat needs the operator role; ask an admin to change this account's role") and skips the chat connection. Read-only status views still load.
  • Companion only acts on a role the gateway actually reported. With no token loaded, the viewer role is a local placeholder, so the connection proceeds and the server-side check still applies. The role is also applied as soon as the auth session loads, not only after the setup status call succeeds.
  • The denial log now says "this action requires the operator role", since /ws/live doesn't run the agent.

Behavior changes

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Tests

Validation

  • dotnet build OpenClaw.Net.slnx --configuration Release: 0 warnings, 0 errors
  • dotnet test OpenClaw.Net.slnx --configuration Release --no-build: OpenClaw.Tests 3,125 passed / 11 skipped / 0 failed (rebased onto fix(gateway): role-gate agent execution and close /apps loopback bypass #256's latest head, a4a5697); NacosLiveAcceptance 25 passed; LayaService 67 passed
  • dotnet run --project samples/OpenClaw.HelloAgent -c Release --no-build

New tests failed first for the right reason:

  • a viewer's /ws/live connection stayed open;
  • Companion attempted the chat connection for a gateway-reported viewer.

Guards cover an operator on /ws/live staying open, and a token-less Companion still attempting to connect.

Review Notes

  • I considered NativeAOT compatibility (no new reflection or serialization)
  • I considered security posture and unsafe defaults
  • I updated docs/tests where needed (docs/AUTHENTICATION.md with zh-CN, CHANGELOG.md)
  • This PR is scoped and does not mix unrelated changes

Commercial or Customer-Driven Contribution Disclosure

Same origin as #256: surfaced during a spec review for AgentQi Mobile. Vendor-neutral gateway and Companion hardening.

Checklist

  • I have read the CONTRIBUTING guidelines
  • My code follows the code style implementation of this project
  • I have added tests that prove my fix is effective or that my feature works
  • All new and existing tests passed locally (dotnet test)
  • I have updated the documentation (README.md, comments) if required
  • I have checked for security implications (input validation, authorization)
  • I have checked the relevant maintainer review checklist
  • I have disclosed whether this directly supports a company or customer use case

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Security
    • /ws/live connections without the operator role are closed with a policy-violation response. The Companion checks the gateway’s reported execution permission before connecting and explains when access is denied; read-only status views remain available. If the gateway omits this permission information, the Companion proceeds with the connection attempt.
  • Documentation
    • Updated authentication guidance to clarify role checks for both WebSocket connections, the /ws/live response for lower-privilege roles, and the execution-permission information reported by /auth/session.

Copilot AI lite review requested due to automatic review settings September 28, 2026 19:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 49cd2d86-71a0-4b39-9697-6fcaa1c1e67c

📥 Commits

Reviewing files that changed from the base of the PR and between 4e64d0e and 6abb971.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • docs/AUTHENTICATION.md
  • docs/zh-CN/AUTHENTICATION.md
  • src/OpenClaw.Companion/ViewModels/MainWindowViewModel.cs
  • src/OpenClaw.Core/Models/AdminApiModels.cs
  • src/OpenClaw.Gateway/Endpoints/AdminEndpoints.Support.cs
  • src/OpenClaw.Gateway/Endpoints/EndpointHelpers.cs
  • src/OpenClaw.Tests/CompanionConnectionTests.cs
  • src/OpenClaw.Tests/GatewayAdminEndpointTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The gateway now reports agent-execution permission through /auth/session and checks the operator role on /ws/live. The Companion checks reported permission before connecting. Account-token verification results are cached within each request.

Changes

Operator-role checks across WebSocket connections

Layer / File(s) Summary
Request-scoped account-token verification
src/OpenClaw.Gateway/Endpoints/EndpointHelpers.cs, src/OpenClaw.Core/Models/GatewayConfig.cs, src/OpenClaw.Tests/EndpointHelpersAuthenticationTests.cs
Account-token verification results and identities are cached in HttpContext.Items and reused for matching tokens within the same request. Tests cover reuse after revocation and rejection in a later request.
Gateway permission response and /ws/live role check
src/OpenClaw.Core/Models/AdminApiModels.cs, src/OpenClaw.Gateway/Endpoints/AdminEndpoints.Support.cs, src/OpenClaw.Gateway/Endpoints/EndpointHelpers.cs, src/OpenClaw.Gateway/Endpoints/WebSocketEndpoints.cs, src/OpenClaw.Tests/GatewayAdminEndpointTests.cs, docs/AUTHENTICATION.md, docs/zh-CN/AUTHENTICATION.md, CHANGELOG.md
/auth/session reports whether the caller can execute the agent. /ws/live closes connections below the operator role with policy-violation status before starting the live session. Tests, authentication documentation, and the changelog cover these behaviors.
Check reported permission before Companion connection
src/OpenClaw.Companion/ViewModels/MainWindowViewModel.cs, src/OpenClaw.Tests/CompanionConnectionTests.cs, CHANGELOG.md
The Companion loads admin status before connecting. If the gateway reports CanExecuteAgent as false, it remains disconnected and displays a role-related message. A true or unavailable value does not block the connection attempt. Tests cover these outcomes.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Companion
  participant AuthSessionEndpoint
  participant Gateway
  participant ChatWebSocket
  Companion->>AuthSessionEndpoint: Request /auth/session
  AuthSessionEndpoint->>Gateway: Resolve agent-execution permission
  Gateway-->>AuthSessionEndpoint: Return permission
  AuthSessionEndpoint-->>Companion: Return canExecuteAgent
  alt Permission is false
    Companion-->>Companion: Remain disconnected and show role message
  else Permission is true or unavailable
    Companion->>ChatWebSocket: Attempt chat connection
  end
Loading

Suggested reviewers: geffzhang

Merge Risk: ⚪ Minimal · up to 6abb9

The change adds an operator-role check to /ws/live and lets Companion stop before connecting when the gateway reports the caller cannot run the agent. Gateway enforcement remains authoritative, and older gateways that omit the field still connect. No blocking merge risk was identified.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 6abb9

The live connection gains a server-enforced role check, while Companion uses the Gateway’s permission report to avoid connections it knows will be denied. No material increase in access was established. Token decisions can remain unchanged during a single request, so immediate revocation is not guaranteed.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The protected sink is the Gateway’s live-session bridge, which can consume its provider credentials. The new check limits access by the Gateway’s account role or explicit viewer-execution policy; the evidence does not establish a broader tenant or deployment-wide exposure.

Trust Boundaries and Controls

  • observed — WebSocket validation checks request authorization and rate limits; /ws/live then makes a separate agent-execution decision before entering the live bridge. Companion’s permission check cannot grant access through that Gateway check.
  • observed — A second check of the same token within one request can reuse an identity recorded before revocation; a separate request verifies again and rejects the revoked token.

Resilience and Maintainability Implications

  • inferred — The request cache makes the new WebSocket role decision a snapshot for that request rather than a fresh account-state read. Base /ws/live had no subsequent role gate, so the reviewed evidence does not establish greater effective live-session access from this timing window.

Hardening Proposals

  • proposed — If role downgrades or token revocation must take effect between handshake validation and privileged admission, revalidate account state at that boundary or explicitly define the request-snapshot revocation guarantee.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 9 files. (3 skipped: 3… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the two primary changes: role-gating /ws/live and checking the gateway-reported role before Companion connects.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 32 functions across 9 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Telli and others added 3 commits September 28, 2026 14:54
Account-token verification runs PBKDF2 (120,000 iterations, about 17 ms
of CPU) inside OperatorAccountService's global lock and then rewrites
the accounts file. The role checks added in this branch meant that
/v1/*, /apps/chat, A2A, /ws, /ws/live, and mutating MCP tools verified
the same token twice per request, halving account-token throughput on
those surfaces.

Cache the verification outcome in HttpContext.Items for the rest of the
request. Revocation, disabling, and role changes still apply from the
next request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
/ws/live admitted any authenticated role. It runs no tools, but it opens
a live session on the gateway's provider credentials, so a viewer token
could spend them. It now uses CanExecuteAgent like /ws and closes below
operator with 1008.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Companion opened the chat socket before loading the account's role, so
a viewer was admitted and then closed by the gateway. It now loads the
role first; when the gateway reports a role below operator it explains
that chat needs the operator role instead of connecting, and keeps the
read-only status views. A role that is only the no-token placeholder
does not block the connection, and the role is applied as soon as the
auth session loads rather than after the setup status call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Telli

Telli commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Reopening to run CI now that this PR targets main.

@Telli Telli closed this Sep 28, 2026
@Telli Telli reopened this Sep 28, 2026

@Telli Telli left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the role gate and Companion connection flow. One compatibility issue needs a fix before the documented viewer migration setting works consistently.

Comment thread src/OpenClaw.Companion/ViewModels/MainWindowViewModel.cs Outdated
…role

Companion's preflight refused to open chat for any gateway-reported role
below operator. With Security.AllowViewerAgentExecution on, the gateway
still admits those viewers, so the documented migration switch did not
work for Companion users.

GET /auth/session now reports canExecuteAgent, computed by the same rule
CanExecuteAgent enforces, including the opt-out. Companion blocks only
when the gateway says false; when the field is absent (older gateways)
it connects and lets the gateway decide.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
public void Dispose()
{
try { Directory.Delete(_storagePath, recursive: true); }
catch { }
public void Dispose()
{
try { Directory.Delete(_storagePath, recursive: true); }
catch { }

private MainWindowViewModel CreateViewModelWithAuthSession(string authSessionJson)
{
var dir = Path.Combine(Path.GetTempPath(), "openclaw-companion-connection-tests", Guid.NewGuid().ToString("N"));

public sealed class EndpointHelpersAuthenticationTests : IDisposable
{
private readonly string _storagePath = Path.Combine(Path.GetTempPath(), "openclaw-endpoint-auth-tests", Guid.NewGuid().ToString("N"));
@Telli
Telli merged commit c38de0b into main Sep 29, 2026
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants