Skip to content

sensing-server: /api/v1/stream/pose WebSocket is unreachable when RUVIEW_API_TOKEN is set (browsers cannot send Authorization on WS upgrade) #1310

Description

@erichkusuki

Summary

When bearer auth is enabled (RUVIEW_API_TOKEN set, #443), the Live Demo tab can never connect: its pose stream lives at /api/v1/stream/pose, which falls under the protected /api/v1/* prefix — but the browser WebSocket constructor cannot attach an Authorization header to the upgrade request. The UI shows:

Failed to start: WebSocket connection failed: Unknown error

/ws/sensing was already deliberately exempted for exactly this reason (see the module docs in bearer_auth.rs: "/ws/sensing and /ui/* are served to local browsers that can't easily inject headers"). The same reasoning applies to /api/v1/stream/pose, which was presumably missed because it sits inside the protected prefix.

Repro

  1. docker run -e RUVIEW_API_TOKEN=<token> ... ruvnet/wifi-densepose:latest
  2. Open the UI, go to Live Demo → connection fails with the error above; server returns 401 on the WS upgrade.

Fix (verified locally, happy to send a PR)

v2/crates/wifi-densepose-sensing-server/src/bearer_auth.rs: add a narrow exempt-path list checked alongside the prefix:

const EXEMPT_PATHS: &[&str] = &["/api/v1/stream/pose"];
...
if !path.starts_with(PROTECTED_PREFIX) || EXEMPT_PATHS.contains(&path) {
    return next.run(request).await;
}

plus a regression test asserting the pose stream stays reachable without a token while other /api/v1/* paths remain 401. (Deliberately not a query-string token — the existing CWE-598 regression test correctly forbids that channel.)

Related UI gap (mention or split as preferred)

Even for plain REST, the bundled UI has no way to supply the token: ui/services/api.service.js has setAuthToken(), but nothing ever calls it and there is no settings field for it — so enabling RUVIEW_API_TOKEN currently breaks the entire dashboard (constant HTTP 401 on /api/v1/*). We added a "API Access" token field to the QuickSettings panel (stores in localStorage, applies before first request) and can include that in the PR too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions