#22081: [Bug]: Preserve approved scope baseline through token rotation
app: web-ui
gateway
size: S
Cluster:
Device Pairing and Gateway Fixes
## Summary
Fixes #22067.
### Problem
`PairedDevice.scopes` was being used as the authorization baseline for scope-upgrade checks during reconnect. When a token is rotated with reduced scopes, that value could shrink and cause subsequent reconnects with previously granted scopes to return `pairing required`, even though the user had already approved a broader scope set.
### What changed
- Added `approvedScopes?: string[]` to `PairedDevice` in `src/infra/device-pairing.ts`.
- On pairing approval, initialize `approvedScopes` from the previously known scope set plus request scopes.
- On `rotateDeviceToken`, preserve/expand `approvedScopes` independently from `scopes` so the live token scope can narrow without losing the approved ceiling.
- Updated websocket scope-upgrade checks in `src/gateway/server/ws-connection/message-handler.ts` to evaluate upgrade permission against `paired.approvedScopes` when available, with fallback to legacy `paired.scopes`.
- Added regression tests:
- `src/infra/device-pairing.test.ts`: ensures approved scope baseline persists across multiple rotations.
- `src/gateway/server.auth.e2e.test.ts`: reproduces rotate-downscope then reconnect with broader scopes and verifies success.
### Safety / compatibility
- Backward compatible: records without `approvedScopes` continue to use existing behavior.
- No schema/API contract changes: this is internal paired metadata evolution.
- Rotation semantics are preserved: current token `scopes` still represent the active session scope and can narrow as before.
### Validation
- `pnpm vitest run --config vitest.unit.config.ts src/infra/device-pairing.test.ts`
- `pnpm vitest run --config vitest.e2e.config.ts src/gateway/server.auth.e2e.test.ts`
- `pnpm exec oxfmt --check src/gateway/server.auth.e2e.test.ts src/gateway/server/ws-connection/message-handler.ts src/infra/device-pairing.ts src/infra/device-pairing.test.ts`
- `pnpm exec oxlint --type-aware src/infra/device-pairing.ts src/infra/device-pairing.test.ts src/gateway/server.ws-connection/message-handler.ts src/gateway/server.auth.e2e.test.ts`
### Confidence score
- **10/10**
- This is a narrow, behavior-preserving change with direct regression coverage around the exact failure path.
- Logic is localized, deterministic, and includes compatibility handling for all pre-existing paired records.
- No unrelated behavioral surface was touched.
Most Similar PRs
#23708: fix(gateway): auto-approve scope upgrades for loopback clients
by widingmarcus-cyber · 2026-02-22
77.0%
#16827: fix: allow device tokens with empty scopes to accept requested scopes
by MisterGuy420 · 2026-02-15
76.8%
#22838: fix(gateway): auto-approve loopback pairing for scope/role upgrades
by GodsBoy · 2026-02-21
76.6%
#17425: fix(gateway): auto-approve scope/role upgrades for already-paired d...
by sauerdaniel · 2026-02-15
76.3%
#22280: fix(gateway): silently auto-approve local paired-device scope upgrades
by abhishekp76 · 2026-02-21
75.9%
#22312: Fix legacy paired metadata handling for reconnect scope compatibility
by AIflow-Labs · 2026-02-21
75.3%
#21664: fix(gateway): require re-pairing for legacy devices that lack scope...
by AI-Reviewer-QS · 2026-02-20
74.5%
#23690: fix(gateway): subagent sessions fail with pairing required on loopb...
by yinghaosang · 2026-02-22
74.1%
#22587: fix(gateway): silently auto-approve local paired-device scope upgrades
by abhishekp76 · 2026-02-21
73.4%
#23503: fix: preserve pairing state on device token mismatch + migrate lega...
by dorukardahan · 2026-02-22
73.3%