Files
Jamie Pine f2cf2a729d Add "Log in with browser" cloud device login (#812)
* Add "Log in with browser" cloud device login

Connects the desktop app to Voicebox Cloud without the user ever handling an
API key. One button in Settings → General opens the system browser to
voicebox.sh, the user authorizes while signed in, and the credential lands
back in the app automatically.

Backend (FastAPI):
- /cloud/login/start opens the browser to the cloud authorize page with a
  state we mint; the existing loopback server catches the redirect at
  /cloud/callback and exchanges the one-time code (server-to-server, over TLS)
  for a voicebox_ API key, verifies it against the API, and stores it.
- /cloud/status and /cloud/disconnect back the settings UI.
- state round-trip guards against login-CSRF; the key never crosses a browser
  URL and is never exposed to the frontend (status returns a prefix only).
- CloudSettings singleton row; config gains VOICEBOX_CLOUD_URL /
  VOICEBOX_CLOUD_API_URL (default the prod hosts, overridable for dev).

Frontend (React):
- CloudSection in Settings → General: "Log in with browser", polls status,
  shows the connected device + a dashboard link. API keys are the advanced
  path only, surfaced in the web dashboard.

The key is stored in the local app DB for now; OS keychain is a marked
follow-up.

* Address review feedback on cloud login

- time out status polling after 2 min so an abandoned browser flow
  doesn't leave the button stuck on "Waiting for browser…"
- handle non-JSON / non-object payloads from the exchange and account
  endpoints instead of 500ing after the state is consumed
- make singleton row creation race-safe (IntegrityError -> re-query)
- clear device_name on disconnect along with the rest of the metadata
- serve the dashboard URL from /cloud/status so the Manage link follows
  VOICEBOX_CLOUD_URL instead of hardcoding production
- keep a "Disconnecting…" label on the disconnect button while pending

* Remove orphaned react-qr-code entries from lockfile

bun.lock was out of date with package.json (react-qr-code was removed
without reinstalling), failing the frozen-lockfile install in CI.
2026-07-05 03:18:30 -07:00
..