mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-19 14:50:38 -07:00
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.
This commit is contained in:
@@ -50,6 +50,8 @@ import type {
|
||||
MCPClientBinding,
|
||||
MCPClientBindingListResponse,
|
||||
MCPClientBindingUpsert,
|
||||
CloudLoginStartResponse,
|
||||
CloudStatus,
|
||||
} from './types';
|
||||
|
||||
function formatErrorDetail(detail: unknown, fallback: string): string {
|
||||
@@ -920,6 +922,21 @@ class ApiClient {
|
||||
|
||||
return response.blob();
|
||||
}
|
||||
|
||||
// Cloud (backup & sync) — browser-based device login. startCloudLogin opens
|
||||
// the system browser server-side; the UI then polls getCloudStatus until the
|
||||
// backend completes the exchange and the link goes live.
|
||||
async getCloudStatus(): Promise<CloudStatus> {
|
||||
return this.request<CloudStatus>('/cloud/status');
|
||||
}
|
||||
|
||||
async startCloudLogin(): Promise<CloudLoginStartResponse> {
|
||||
return this.request<CloudLoginStartResponse>('/cloud/login/start', { method: 'POST' });
|
||||
}
|
||||
|
||||
async disconnectCloud(): Promise<CloudStatus> {
|
||||
return this.request<CloudStatus>('/cloud/disconnect', { method: 'POST' });
|
||||
}
|
||||
}
|
||||
|
||||
export const apiClient = new ApiClient();
|
||||
|
||||
Reference in New Issue
Block a user