openclaw - ✅(Solved) Fix Gateway clears operator scopes for token-authenticated backend clients (regression) [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#56194Fetched 2026-04-08 01:43:47
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1

Commit 3e2b3bd2c5 ("Fix Control UI operator.read scope handling", #53110) regressed the fix from 9c142993b ("fix: preserve operator scopes for shared auth connections").

Backend operator clients that authenticate with a valid gateway token but without device identity (e.g. the AlphaClaw chat bridge using gateway-client / backend mode) have their self-declared scopes unconditionally cleared during the WebSocket handshake. This causes all scoped method calls (e.g. chat.history requiring operator.read) to fail with missing scope: operator.read.

Root Cause

clearUnboundScopes lost its !sharedAuthOk guard when #53110 refactored the call site:

// clearUnboundScopes — no longer checks sharedAuthOk
const clearUnboundScopes = () => {
    if (scopes.length > 0) {
        scopes = [];
        connectParams.scopes = scopes;
    }
};

The call site condition inverts the intent — it clears scopes for authMethod === "token" instead of preserving them:

if (
    !device &&
    (decision.kind !== "allow" ||
        (!controlUiAuthPolicy.allowBypass &&
            !preserveInsecureLocalControlUiScopes &&
            (authMethod === "token" || authMethod === "password" || trustedProxyAuthOk)))
) {
    clearUnboundScopes();
}

The inline comment ("Device-less clients only keep self-declared scopes on the explicit allow path, including trusted token-authenticated backend operators") confirms the intent is to preserve scopes for token auth, but the code does the opposite.

Fix Action

Fix

Restore the !sharedAuthOk guard inside clearUnboundScopes itself (belt-and-suspenders), so token-authenticated connections never have their scopes wiped regardless of call-site logic.

PR fix notes

PR #56195: fix(gateway): preserve operator scopes for shared-auth connections

Description (problem / solution / changelog)

Summary

  • Restores the !sharedAuthOk guard inside clearUnboundScopes that was removed by 3e2b3bd2c5 (#53110), which causes token-authenticated backend clients (e.g. AlphaClaw chat bridge) to have their operator scopes unconditionally stripped during the WS handshake
  • All scoped method calls (chat.history, etc.) fail with missing scope: operator.read even though the connection is properly authenticated

Root Cause

clearUnboundScopes was meant to strip self-declared scopes only for unauthenticated or device-less anonymous connections. The original fix in 9c142993b added && !sharedAuthOk so token/password-authenticated operators keep their scopes. #53110 refactored the call site and removed this guard, regressing the fix.

Test plan

  • Connect as gateway-client / backend / operator with a valid token and no device identity — scopes should be preserved
  • chat.history (requires operator.read) succeeds after the handshake
  • Control UI connections without shared auth still have scopes cleared as before

Closes #56194

Changed files

  • src/gateway/server/ws-connection/message-handler.ts (modified, +1/-1)

Code Example

// clearUnboundScopes — no longer checks sharedAuthOk
const clearUnboundScopes = () => {
    if (scopes.length > 0) {
        scopes = [];
        connectParams.scopes = scopes;
    }
};

---

if (
    !device &&
    (decision.kind !== "allow" ||
        (!controlUiAuthPolicy.allowBypass &&
            !preserveInsecureLocalControlUiScopes &&
            (authMethod === "token" || authMethod === "password" || trustedProxyAuthOk)))
) {
    clearUnboundScopes();
}
RAW_BUFFERClick to expand / collapse

Summary

Commit 3e2b3bd2c5 ("Fix Control UI operator.read scope handling", #53110) regressed the fix from 9c142993b ("fix: preserve operator scopes for shared auth connections").

Backend operator clients that authenticate with a valid gateway token but without device identity (e.g. the AlphaClaw chat bridge using gateway-client / backend mode) have their self-declared scopes unconditionally cleared during the WebSocket handshake. This causes all scoped method calls (e.g. chat.history requiring operator.read) to fail with missing scope: operator.read.

Root Cause

clearUnboundScopes lost its !sharedAuthOk guard when #53110 refactored the call site:

// clearUnboundScopes — no longer checks sharedAuthOk
const clearUnboundScopes = () => {
    if (scopes.length > 0) {
        scopes = [];
        connectParams.scopes = scopes;
    }
};

The call site condition inverts the intent — it clears scopes for authMethod === "token" instead of preserving them:

if (
    !device &&
    (decision.kind !== "allow" ||
        (!controlUiAuthPolicy.allowBypass &&
            !preserveInsecureLocalControlUiScopes &&
            (authMethod === "token" || authMethod === "password" || trustedProxyAuthOk)))
) {
    clearUnboundScopes();
}

The inline comment ("Device-less clients only keep self-declared scopes on the explicit allow path, including trusted token-authenticated backend operators") confirms the intent is to preserve scopes for token auth, but the code does the opposite.

Reproduction

  1. Start a gateway with auth.mode: "token".
  2. Connect a WebSocket client as gateway-client / backend / operator with a valid token, requested scopes ["operator.admin"], and no device identity.
  3. Call chat.history — fails with missing scope: operator.read.

Fix

Restore the !sharedAuthOk guard inside clearUnboundScopes itself (belt-and-suspenders), so token-authenticated connections never have their scopes wiped regardless of call-site logic.

Affected Versions

Introduced in the commit tree leading to 2026.3.24. The original fix (9c142993b) was present since v2026.2.26.

extent analysis

Fix Plan

To fix the issue, we need to restore the !sharedAuthOk guard inside clearUnboundScopes. Here are the steps:

  • Modify the clearUnboundScopes function to include the !sharedAuthOk guard:
const clearUnboundScopes = () => {
    if (scopes.length > 0 && !sharedAuthOk) {
        scopes = [];
        connectParams.scopes = scopes;
    }
};
  • Update the call site condition to correctly preserve scopes for token-authenticated backend operators:
if (
    !device &&
    (decision.kind !== "allow" ||
        (!controlUiAuthPolicy.allowBypass &&
            !preserveInsecureLocalControlUiScopes &&
            (authMethod === "password" || trustedProxyAuthOk)))
) {
    clearUnboundScopes();
}

Note that we removed the authMethod === "token" condition, as we want to preserve scopes for token-authenticated connections.

Verification

To verify the fix, follow these steps:

  • Start a gateway with auth.mode: "token".
  • Connect a WebSocket client as gateway-client / backend / operator with a valid token, requested scopes ["operator.admin"], and no device identity.
  • Call chat.history — it should succeed without throwing a missing scope: operator.read error.

Extra Tips

  • Make sure to test the fix thoroughly to ensure that it does not introduce any regressions.
  • Consider adding additional logging or monitoring to detect similar issues in the future.
  • Review the code changes carefully to ensure that they align with the intended behavior and security policies.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING