Skip to content

fix(auth): surface distinct scope-mismatch reason for device token failures#79296

Closed
hclsys wants to merge 1 commit into
openclaw:mainfrom
hclsys:fix/auth-scope-mismatch-error-code-79292
Closed

fix(auth): surface distinct scope-mismatch reason for device token failures#79296
hclsys wants to merge 1 commit into
openclaw:mainfrom
hclsys:fix/auth-scope-mismatch-error-code-79292

Conversation

@hclsys

@hclsys hclsys commented May 8, 2026

Copy link
Copy Markdown

Summary

When verifyDeviceToken returns reason: "scope-mismatch" (the token's approved scopes do not cover the requested connection scopes), the auth pipeline collapsed this into the generic device_token_mismatch error and message — misleading clients into rotating/reissuing a perfectly valid token.

  • Expand VerifyDeviceTokenResult type to include reason?: string
  • Propagate scope-mismatch as a distinct device_token_scope_mismatch reason
  • Add AUTH_DEVICE_TOKEN_SCOPE_MISMATCH error detail code to ConnectErrorDetailCodes
  • Add "device_token_scope_mismatch" message case: "unauthorized: device token scope mismatch (re-pair this device to request the current scope set)"
  • Add test: verifyDeviceToken returning scope-mismatchauthResult.reason === "device_token_scope_mismatch"

Fixes #79292.

Root cause

macOS/iOS/Control UI clients request 5 scopes (operator.admin, operator.read, operator.write, operator.approvals, operator.pairing). Bootstrap token issuance strips operator.admin and operator.pairing (the allowlist only has 4), so the stored device token has 4 scopes. On reconnect with 5 scopes, verifyDeviceToken at line 896/900 of device-pairing.ts returns { ok: false, reason: "scope-mismatch" }. resolveConnectAuthDecision dropped the reason field (the local VerifyDeviceTokenResult type was { ok: boolean }) and emitted device_token_mismatch — the wrong diagnosis.

Real behavior proof

  • Behavior or issue addressed: When verifyDeviceToken returns reason: "scope-mismatch", resolveConnectAuthDecision lost the reason field (type was { ok: boolean }, no reason field) and emitted generic device_token_mismatch. This PR extends VerifyDeviceTokenResult with reason?: string and branches on "scope-mismatch" to emit device_token_scope_mismatch instead.
  • Real environment tested: Linux (DGX Spark), Node v22.22.0, OpenClaw source checkout at commit 1a7612f0be29147180072a0fb218f44449ce24c6, tsx for direct TypeScript import.
  • Exact steps or command run after this patch: node --import tsx /tmp/proof_79296.mjs where the script calls resolveConnectAuthDecision directly with a verifyDeviceToken stub returning { ok: false, reason: "scope-mismatch" } and asserts the returned authResult.reason.
  • Evidence after fix:
authResult.reason: device_token_scope_mismatch
authOk: false
PASS: scope-mismatch correctly surfaced as device_token_scope_mismatch
  • Observed result after fix: resolveConnectAuthDecision returns authResult.reason === "device_token_scope_mismatch" when verifyDeviceToken returns { ok: false, reason: "scope-mismatch" }. Before this patch the VerifyDeviceTokenResult type had no reason field — the value was silently discarded and the generic device_token_mismatch reason was emitted instead.
  • What was not tested: Live macOS/iOS device reconnect with mismatched scopes against a running gateway with real device pairing. The proof script exercises the exact decision branch directly.

Test plan

  • src/gateway/server/ws-connection/auth-context.test.ts — new test: scope-mismatch reason propagates as device_token_scope_mismatch; all 30 tests pass
  • src/gateway/protocol/connect-error-details.test.ts — all 26 tests pass
  • Additive only — no callers of formatGatewayAuthFailureMessage or readConnectAuthDecisionErrorDetailCode need changes

🤖 Generated with Claude Code

…ilures

When verifyDeviceToken returns reason="scope-mismatch" (the device token's
approved scopes do not cover the requested connection scopes), the auth
pipeline previously collapsed this into the generic device_token_mismatch
error, causing clients to rotate/reissue a perfectly valid token.

Surface a distinct device_token_scope_mismatch reason and error message
("re-pair this device to request the current scope set") so clients can
take the right corrective action. Adds AUTH_DEVICE_TOKEN_SCOPE_MISMATCH
error detail code for protocol-level classification.

Fixes openclaw#79292.
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 8, 2026
@clawsweeper

clawsweeper Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge.

Summary
This PR propagates verifyDeviceToken scope mismatches as device_token_scope_mismatch, adds a matching connect detail code/message, a focused auth test, and a changelog entry.

Reproducibility: yes. for the source-level seam: current main can return scope-mismatch from verifyDeviceToken, and resolveConnectAuthDecision drops that reason. I did not establish a live macOS/iOS/Control UI reconnect reproduction in this read-only review.

Real behavior proof
Needs real behavior proof before merge: The PR body includes terminal output from a direct TypeScript proof with a stubbed verifier, which is source/mock proof rather than observed after-fix behavior from a real gateway/client setup. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, ask a maintainer to comment @clawsweeper re-review.

Next step before merge
Manual follow-up is needed because the PR still needs real gateway/client behavior proof and client handling for the new protocol detail code before merge.

Security
Cleared: The diff only changes auth failure classification, message text, tests, and changelog text; it does not add dependencies, scripts, permissions, or weaken token verification.

Review findings

  • [P2] Wire the new auth code into reconnect gating — src/gateway/protocol/connect-error-details.ts:14
Review details

Best possible solution:

Land the diagnostic propagation together with matching terminal/re-pair handling in the affected TS and native clients, plus redacted real gateway/client evidence showing the scope-mismatch recovery path.

Do we have a high-confidence way to reproduce the issue?

Yes for the source-level seam: current main can return scope-mismatch from verifyDeviceToken, and resolveConnectAuthDecision drops that reason. I did not establish a live macOS/iOS/Control UI reconnect reproduction in this read-only review.

Is this the best way to solve the issue?

No as currently patched: carrying the verifier reason forward is the right narrow server fix, but emitting a new public detail code without updating client recovery gates makes the reported recovery path incomplete.

Full review comments:

  • [P2] Wire the new auth code into reconnect gating — src/gateway/protocol/connect-error-details.ts:14
    This PR starts returning AUTH_DEVICE_TOKEN_SCOPE_MISMATCH, but existing stale-device-token and reconnect gates only special-case AUTH_DEVICE_TOKEN_MISMATCH or other known terminal auth codes. Control UI and the TS gateway client would treat the new code as an unknown recoverable auth error and keep reconnecting instead of pausing for repair; native clients also need the enum/recovery classification before this code is emitted.
    Confidence: 0.88

Overall correctness: patch is incorrect
Overall confidence: 0.88

What I checked:

  • current-main diagnostic collapse: Current main types the device-token verifier result as { ok: boolean } and maps failed explicit device-token checks to device_token_mismatch, so scope-mismatch is dropped before formatting. (src/gateway/server/ws-connection/auth-context.ts:35, ff860dcf6eb2)
  • verifier reason exists: verifyDeviceToken already returns { ok: false, reason: "scope-mismatch" } when stored or requested scopes are outside the approved device baseline. (src/infra/device-pairing.ts:915, ff860dcf6eb2)
  • scope mismatch is plausible: Bootstrap handoff operator scopes exclude operator.admin and operator.pairing, while Control UI and Swift operator clients request those scopes. (src/shared/device-bootstrap-profile.ts:13, ff860dcf6eb2)
  • client gates still know only the old terminal code: Control UI and the TS gateway client pause reconnect and clear stale stored device tokens only for AUTH_DEVICE_TOKEN_MISMATCH; the PR changes only five server/protocol/test/changelog files, so the new code is not wired into those gates. (ui/src/ui/gateway.ts:93, 1a7612f0be29)
  • proof is direct seam proof, not live behavior: The updated PR body supplies terminal output from a direct resolveConnectAuthDecision script with a stubbed verifyDeviceToken, but it explicitly does not test a real gateway/client reconnect with mismatched scopes. (1a7612f0be29)
  • feature history: Recent public commit history for the central gateway auth, device-pairing, protocol detail, and client reconnect files points mainly to steipete, with protocol detail work by obviyus and native gateway/auth work by ngutman.

Likely related people:

  • steipete: Recent commits cover device-token scope containment, narrowed token scopes on upgrade, stale device-token reconnect handling, and gateway auth helper maintenance adjacent to this PR. (role: recent maintainer; confidence: high; commits: c9053ff20853, 885806d5ca8d, 8bbb143ab87e; files: src/infra/device-pairing.ts, ui/src/ui/gateway.ts, src/gateway/client.ts)
  • obviyus: Recent work added and enriched pairing connect error details in the same protocol detail module that this PR extends with a new auth detail code. (role: adjacent protocol owner; confidence: medium; commits: f070a92e19fb, 4e01916a7e1b, 66e1c3982d26; files: src/gateway/protocol/connect-error-details.ts)
  • ngutman: History shows work on gateway auth selection and native GatewayChannel error UX/bootstrap-token handling, which is relevant to any new auth detail code consumed by Swift clients. (role: native/auth adjacent maintainer; confidence: medium; commits: 017bc5261c9b, 6380c872bcc2, a9140abea6d4; files: src/gateway/server/ws-connection/auth-context.ts, apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayChannel.swift, apps/shared/OpenClawKit/Sources/OpenClawKit/GatewayErrors.swift)

Remaining risk / open question:

  • The new protocol detail code is not yet handled by clients that suppress reconnect loops or clear stale stored device tokens.
  • The supplied proof is a direct function call with a stubbed verifier, not a real gateway/client reconnect using mismatched scopes.

Codex review notes: model gpt-5.5, reasoning high; reviewed against dce9261415f7.

@hclsys

This comment was marked as low quality.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. labels May 8, 2026
@hclsys

This comment was marked as low quality.

BunsDev added a commit that referenced this pull request May 11, 2026
Preserve full shared-token operator sessions while reusing bootstrap/cached device-token scopes for handoff reconnects.

Surface device-token scope mismatches as AUTH_SCOPE_MISMATCH and stop reconnect retry loops without clearing valid stored tokens.

Fixes #79292.
Supersedes #79314, #79296, #79295.
steipete pushed a commit that referenced this pull request May 12, 2026
Preserve full shared-token operator sessions while reusing bootstrap/cached device-token scopes for handoff reconnects.

Surface device-token scope mismatches as AUTH_SCOPE_MISMATCH and stop reconnect retry loops without clearing valid stored tokens.

Fixes #79292.
Supersedes #79314, #79296, #79295.
@clawsweeper

clawsweeper Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper could not start a re-review for this item.

Reason: re-review requires an open issue or PR.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Preserve full shared-token operator sessions while reusing bootstrap/cached device-token scopes for handoff reconnects.

Surface device-token scope mismatches as AUTH_SCOPE_MISMATCH and stop reconnect retry loops without clearing valid stored tokens.

Fixes openclaw#79292.
Supersedes openclaw#79314, openclaw#79296, openclaw#79295.
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Preserve full shared-token operator sessions while reusing bootstrap/cached device-token scopes for handoff reconnects.

Surface device-token scope mismatches as AUTH_SCOPE_MISMATCH and stop reconnect retry loops without clearing valid stored tokens.

Fixes openclaw#79292.
Supersedes openclaw#79314, openclaw#79296, openclaw#79295.
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Preserve full shared-token operator sessions while reusing bootstrap/cached device-token scopes for handoff reconnects.

Surface device-token scope mismatches as AUTH_SCOPE_MISMATCH and stop reconnect retry loops without clearing valid stored tokens.

Fixes openclaw#79292.
Supersedes openclaw#79314, openclaw#79296, openclaw#79295.
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 gateway Gateway runtime proof: supplied External PR includes structured after-fix real behavior proof. size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(auth): operator scope mismatch silently rejected as device token mismatch

1 participant