fix(helper-auth): rebind app-password on stale device ID instead of returning 409#1
Open
terafin wants to merge 2 commits into
Open
fix(helper-auth): rebind app-password on stale device ID instead of returning 409#1terafin wants to merge 2 commits into
terafin wants to merge 2 commits into
Conversation
d853056 to
0dc6a28
Compare
Helper save uploads were failing with HTTP 409 "app password is already bound to another device" even when the app-password was correctly bound to the requesting device. Root cause: in authenticateHelperKey, CHECK 3 rejected the request whenever the app-password record's stored BoundDeviceID differed from the device resolved by fingerprint AND that stale device ID still existed in memory. After an RSM restart reloads security_device_state.json, the same physical machine can be re-created (or re-keyed) under a new device ID, so the stored ID and the resolved ID legitimately diverge — producing a spurious 409 on every upload. Fix: - CHECK 3 now only returns 409 when the stale device is a genuinely different machine (its fingerprint does not match). Otherwise it falls through to the existing rebind logic, which repoints the app-password at the current device ID. - bindAppPasswordToDeviceLocked now deletes the app-password it displaces when no other device still references it, so unbound/unusable records don't accumulate as orphans across repeated (re)bindings. Adds unit tests covering: correctly-bound upload succeeds; stale device ID with matching fingerprint rebinds; genuinely different device still 409s; displaced password is cleaned up; password referenced elsewhere is kept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0dc6a28 to
09d3d61
Compare
…ice_type drift)
A helper can present a different device_type per source while keeping a stable
fingerprint — the SGM Steam Deck helper enrolls as device_type "steamdeck" but
uploads saves tagged with the source emulator type ("retroarch"). authenticateHelperKey
keyed identity on (device_type, fingerprint), so the upload spawned a second device
and returned 409 "app password is already bound to another device" on every save.
Treat the fingerprint as the stable per-device identity and device_type as variable
metadata: when the presented fingerprint matches the bound one, keep the bound identity
(same physical device, rebind cleanly). A different fingerprint is still rejected.
Adds regression tests for both cases.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Helper save uploads fail with HTTP
409 "app password is already bound to another device"even when the app-password is correctly bound to the requesting device. This breaks uploads forsgm-steamdeck-helper(and any helper):security_device_state.jsonshows the correct binding, yet every upload 409s.Root cause
In
authenticateHelperKey(backend/cmd/server/helper_auth.go), CHECK 3 compares the app-password record's storedBoundDeviceIDagainst the device resolved by fingerprint (findDeviceByIdentityLocked) and returns 409 whenever they differ and the stale device ID still exists in memory:After an RSM restart reloads
security_device_state.json, the same physical machine can come back under a new device ID (re-created / re-keyed / inconsistent state). The storedBoundDeviceIDand the fingerprint-resolvedboundDevice.IDthen legitimately diverge, so this guard 409s on a device that is actually the rightful owner — even though the code immediately below CHECK 3 already knows how to rebind the record to the current device.Fix
CHECK 3 rebind — only return 409 when the stale device is a genuinely different machine (its fingerprint doesn't match the resolved device). Otherwise fall through to the existing rebind path, which repoints the app-password at the current device ID:
Orphan cleanup —
bindAppPasswordToDeviceLockedpreviously unbound a displaced app-password but left the dangling record ina.appPasswordsforever. It now deletes that record when no other device references it (deleteAppPasswordIfOrphanedLocked), preventing unbound/unusable app-passwords from accumulating across repeated (re)bindings.Tests
New
backend/cmd/server/helper_auth_device_binding_test.go:go test ./cmd/server/passes in full;gofmtandgo vetare clean.Notes for reviewers
The original issue report proposed a second root cause — that
bindAppPasswordToDeviceLockednever sets the reciprocaldevice.AppPasswordID. That doesn't apply to the current source: the function already sets the reciprocaldevice.BoundAppPasswordID(security_state.go), andappPasswordIDForDeviceLockedkeys off the app-password'sBoundDeviceID, not a device-side field. The verifiable over-eager 409 is the CHECK 3 path fixed here.🤖 Generated with Claude Code
Update — also fixes the device_type drift (CHECK 1) + tested on real hardware
The stale-device-ID rebind (CHECK 3) alone wasn't enough: the SGM Steam Deck helper
enrolls with
device_type="steamdeck"but uploads saves tagged with the source'semulator type (
device_type="retroarch"), keeping a stablefingerprint. BecauseauthenticateHelperKeykeyed identity on(device_type, fingerprint), every uploadresolved/created a different device and 409'd. This adds a CHECK 1 fix: treat the
fingerprint as the device identity and
device_typeas variable metadata — samefingerprint ⇒ same device (rebind cleanly), different fingerprint ⇒ still rejected.
Tested on real hardware: Steam Deck running
sgm-steamdeck-helperv0.4.16 (RetroDecksource) against a build of this branch. Before: every save
409 Conflict. After:sync … uploaded=3 … errors=0, and a second sync reportsin_sync=3. Added unit testsfor both the same-fingerprint/different-device_type (success) and different-fingerprint
(reject) cases.