Repository navigation
fix(cloudxr): deliver the OOB control token in the URL fragment - #1318
gareth-morgan-nv wants to merge 8 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
✅ Preview deployed: https://NVIDIA.github.io/IsaacCapture/preview/pr-1318/ Built from commit 9d3b091. It may take a minute for the updated preview to appear. |
The headset bookmark put controlToken in the URL query, so it was sent in the request target and visible to the hosting server and proxy logs. The bookmark and startup banner now put it in the fragment (#<route>?controlToken=...), which the browser never sends. The web client lifts the token from the fragment (or a legacy query) before React canonicalizes the hash, stores it in sessionStorage keyed by hub host:port so reloads keep working, and strips it from the address bar. A newly supplied token replaces a stored one; one endpoint's token is never reused for another. When sessionStorage is denied the token is kept for the current page only. --host-client now also refuses a cached bundle.js that predates fragment support instead of silently failing to authenticate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
…tored one When sessionStorage rejects the write of a newly supplied token, the old stored value must not shadow the fresh in-memory one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
…or any OOB host-client Add a Playwright spec that drives the real App (app-mock build) against a real OOBControlHub: fragment token -> authenticated register -> URL cleanup -> reload -> authenticated register again, with the token absent from page requests and Referer; a legacy query token; and rejection without a token. The --host-client bundle check for fragment support now applies whenever OOB is set up (including hub-only mode), independent of the health-probe requirement, so an older cached bundle cannot silently fail to authenticate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
…ght calls Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
Embedded owners bypass the CLI banner, the only place the generated token was printed, so they had no way to authenticate hub requests or build a headset URL. Add a control_token property returning the value the hub enforces (None without setup_oob); the token is still never logged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
… hub-only accessor Hub-only mode delivers no bookmark, so operators need the token: document that the banner prints it on stdout (service.log under service start) and that CONTROL_TOKEN avoids the lookup. Run the accessor/hub agreement test with and without TELEOP_OOB_HUB_ONLY. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
…o the manual URL step Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Gareth Morgan <gmorgan@nvidia.com>
1028e67 to
9d3b091
Compare
Summary
The headset bookmark carried
controlTokenin the URL query, so the token was sent in the HTTP request target and was visible to the hosting server and proxy logs. It now travels in the URL fragment, which the browser never sends in a request orReferer.Builds on #1316, which has merged (it uses that PR's token resolver and bookmark builder).
What changed
build_headset_bookmark_urland the startup banner put the token in the fragment:#<route>?controlToken=<urlencoded>(#?controlToken=…when no route is set). The token is omitted when OOB is disabled.helpers/oobControlToken.ts, called fromindex.tsxbefore React mounts (so beforeAppcanonicalizes the hash): reads the fragment token (fragment wins over a legacy?controlToken=query), stores it insessionStoragekeyed by hubserverIP:port, and strips it from the address bar, keeping the route, other params and history state. A newly supplied token replaces a stored one; another endpoint's token is never reused. IfsessionStorageis denied the token lasts for the current page only, with a console warning.Appreads the token throughgetOobControlToken.--host-clientnow refuses a cachedbundle.jsthat predates fragment support (same check as the existing health-protocol marker), instead of silently failing to authenticate.oob_teleop_control.rstdocuments the fragment bookmark, the remaining exposure and the older-client incompatibility.Compatibility
A client build older than this change reads only
?controlToken=from the query and will not authenticate against a fragment bookmark. The hosted client is versioned, so ship this client before relying on generated bookmarks with a cached or pinned older build. A hand-built?controlToken=link still works, but that URL has already been sent in the request.Not covered
The fragment is still visible to the page's own JavaScript,
adbcommand arguments and anyone who sees the URL.Validation
helpers/oobControlToken.test.ts(11) and the real-hubcontrolChannel.test.tsOobControlTokenTest.spec.js(3 passed): the realApp.tsx(app-mock build) against a realOOBControlHub: fragment token -> authenticated register -> URL cleanup with route kept -> page reload -> authenticated register again, token absent from every page request andReferer; legacy query token; no token is rejected. DisablingcaptureOobControlToken()makes the first two fail.npm run build:bundle.jscontains theoobControlToken:marker, and the host preflight accepts it; an older bundle is accepted for ordinary hosted streaming and rejected for OOB, including hub-only modetests/python/core/cloudxr:test_oob_teleop_env.py,test_oob_teleop_adb.py,test_oob_teleop_hub.py,test_webclient.py,test_wss_static_client.py(212 passed)SKIP=check-copyright-year pre-commit run --all-filesmain) and a real CloudXR streamam start) will be added once test(cloudxr): reusable fake-adb mock (FakeAdb/mock_adb) #1147 (the fake-adb test double) merges.ControlPanelPositionTestx3,CloudXRComponentTestx1) reproduce identically on an unmodifiedmaincheckout in this environmentTest plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation