← Back to PRs

#17379: fix: restore device token priority in device-auth mode

by Limitless2023 open 2026-02-15 18:01 View on GitHub →
gateway stale size: XS
Fixes #17270, #16820, #16862, #17223, #17233 --- ## Problem Commit [d8a2c80cd](https://github.com/openclaw/openclaw/commit/d8a2c80cd) ("fix(gateway): prefer explicit token over stored auth") introduced a **token priority regression** that breaks device authentication for all non-localhost paired devices. **Symptom:** After upgrading from v2026.2.13 to v2026.2.14, all previously paired devices (Node, CLI, browser) fail to connect with: ``` unauthorized: device token mismatch (rotate/reissue device token) ``` Downgrading to v2026.2.13 **immediately fixes** the issue without any config changes. **Affected platforms:** Ubuntu, macOS, PopOS, Linux arm64 (#16820, #16862, #17223, #17233) --- ## Root Cause ### The Regression (d8a2c80cd) The commit changed `src/gateway/client.ts` line 193: ```diff - // v2026.2.13 — device token takes priority - const authToken = storedToken ?? this.opts.token ?? undefined; + // v2026.2.14 — config/env token takes priority + const authToken = this.opts.token ?? storedToken ?? undefined; ``` **Intent:** Allow CLI `--token` to override stored device tokens. **Problem:** `this.opts.token` contains **both**: 1. **Explicit CLI overrides** (`--token <value>`) ← should take priority ✅ 2. **Config file defaults** (`gateway.token`) ← should NOT override device tokens ❌ The commit treated both as "explicit" and broke device authentication. --- ### Why It Breaks Device Auth **Device pairing flow:** 1. Device pairs with gateway → receives **device-specific token** 2. Token stored locally in `~/.openclaw/auth/devices/<deviceId>.json` 3. Device connects → should send **stored device token** **In v2026.2.14:** - Device has `storedToken` (device-specific) ✅ - Config has `gateway.token` (shared gateway token) ⚠️ - **Priority:** `opts.token ?? storedToken` → sends **shared token** ❌ - Gateway rejects: **"device token mismatch"** ❌ **In v2026.2.13:** - **Priority:** `storedToken ?? opts.token` → sends **device token** ✅ - Gateway accepts ✅ --- ## Solution **Restore device token priority when `deviceIdentity` is present:** ```typescript const authToken = this.opts.deviceIdentity && storedToken ? storedToken // ✅ Device-auth mode: use device-specific token : this.opts.token ?? storedToken ?? undefined; // Shared-auth or fallback ``` **Logic:** - **Device-auth mode** (`deviceIdentity` + `storedToken` exist): - Use stored device token (ignore config `gateway.token`) - Ensures paired devices continue working after config changes/upgrades - **Shared-auth mode** (no `deviceIdentity` or no `storedToken`): - Use `opts.token` (CLI `--token` or config `gateway.token`) - Fallback to `storedToken` if available **Why this is correct:** - When a device is paired, `deviceIdentity` is set (contains `deviceId`, `deviceName`) - Paired devices **must** use their device-specific token (not shared token) - Config `gateway.token` is for **unpaired clients** or **shared authentication** --- ## Impact ### ✅ Fixed - Paired devices (Node, CLI, browser) authenticate successfully after v2026.2.14 upgrade - Device tokens are respected in device-auth mode - Config `gateway.token` still works for unpaired clients - **No manual token rotation or re-pairing required** ### ✅ Preserves Original Intent - CLI `--token` **still overrides** device token when device is **not yet paired** - After pairing, device token takes priority (correct security model) ### 📝 Known Limitation If user explicitly wants to override a paired device's token via CLI `--token`: 1. Clear device token: `rm ~/.openclaw/auth/devices/<deviceId>.json` 2. Then use `openclaw connect --token <new-token>` **This is acceptable because:** - Overriding paired device tokens is rare (users normally re-pair instead) - Device auth is **intentionally sticky** (security best practice) - Users can always re-pair to change tokens --- ## Testing **Manual verification:** 1. Pair a device on v2026.2.13 (or clean install + this PR) 2. Verify stored token: `cat ~/.openclaw/auth/devices/<deviceId>.json` 3. Add `gateway.token` to config (simulating shared token) 4. Reconnect → uses device token, ignores config token ✅ **Regression check:** 1. Test on v2026.2.14 (before this PR) → fails ❌ 2. Apply this PR → succeeds ✅ 3. Upgrade path: v2026.2.13 → v2026.2.14 + PR → no breakage ✅ --- ## Related Issues This PR fixes **5 issues** sharing the same root cause (d8a2c80cd priority flip): - #17270 - Device token auth regression in v2026.2.14 - #16820 - PopOS: device token mismatch after update - #16862 - Unauthorized: device token mismatch - #17223 - systemd service: device token mismatch - #17233 - Safari webchat: requires macOS logout after v2026.2.14 All reports describe the same pattern: 1. Works on v2026.2.13 ✅ 2. Breaks on v2026.2.14 ❌ 3. Downgrade to v2026.2.13 → works again ✅ **Root cause:** Token priority flip in d8a2c80cd. --- **Note:** This is a clean re-submission of the previously closed PR #17296 (which had unrelated commits). This branch contains only the single targeted fix. <!-- greptile_comment --> <h3>Greptile Summary</h3> Fixes a token priority regression introduced in v2026.2.14 (commit d8a2c80cd) that broke device authentication for all non-localhost paired devices. The original commit flipped `storedToken ?? opts.token` to `opts.token ?? storedToken`, causing config-file `gateway.token` values to override device-specific tokens — resulting in "device token mismatch" errors across platforms. This PR restores device token priority by preferring `storedToken` when both `deviceIdentity` and a stored token are present. - **Fixes 5 related issues** (#17270, #16820, #16862, #17223, #17233) sharing the same root cause - The effective behavior matches v2026.2.13: stored device tokens take priority, with `opts.token` as fallback when no stored token exists - **No unit tests** were added for the token priority logic in `sendConnect()`. Given this is a regression fix for a subtle priority ordering bug, a unit test verifying the correct token selection under both conditions (stored token present vs. absent) would help prevent future regressions. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — it correctly restores the pre-regression device token priority behavior with a minimal, targeted change. - The fix is a single-line logic change that effectively restores the pre-regression behavior. The deviceIdentity guard in the ternary is always truthy (as noted in prior review), making the condition equivalent to checking storedToken first and falling back to opts.token — which matches the original logic. The change is correct and addresses a real regression. Score is 4 rather than 5 due to the absence of unit tests for this specific token priority logic, which is the exact area where the original regression occurred. - No files require special attention beyond the already-discussed `deviceIdentity` truthy guard in `src/gateway/client.ts`. <sub>Last reviewed commit: 51d7026</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs