mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-17 17:28:58 +00:00
## Summary Behind `unified_cloud_auth` (default OFF), flip the two token accessors so every cloud request rides the single Cloud JWT minted in PR2. This is the consumer-flip phase of FE-950: PR2 built the dormant `unifiedToken` slot; this PR makes consumers read it — and surfaces the permanent-auth-failure path that the flip turns from graceful degradation into a hard stop. Stacked on #12704 (PR2), now merged; base is `main`. ## Changes - **What**: - `getAuthHeader()` — flag ON returns `{ Authorization: Bearer <unifiedToken> }` (or `null` if unminted), with **no** Firebase/API-key fallback. Flag OFF keeps the exact workspace → Firebase → API-key cascade. - `getAuthToken()` — flag ON returns the unified Cloud JWT (or `undefined`); flag OFF keeps workspace → Firebase. - Both accessors are the single seam every cloud consumer already routes through, so the flip propagates automatically with **no edits** to `fetchApi` (`scripts/api.ts`), `/customers/*` (authStore), `workspaceApi`, the WebSocket (`api.ts:568`), or backend-node auth (`app.ts:1593`). - **Surface permanent auth failures** (answers @pythongosssss's review on PR2). Under the flag there is no Firebase fallback, so a silent `clearUnifiedContext()` wipe would strand every cloud request until manual re-login — unlike the legacy path, which degrades to the Firebase token. `refreshUnified()` and `mintAtLogin()` now emit a user-facing error toast (keyed by error code off the existing `workspaceAuth.errors` i18n) on the permanent codes (`ACCESS_DENIED` / `WORKSPACE_NOT_FOUND` / `INVALID_FIREBASE_TOKEN` / `NOT_AUTHENTICATED`). `mintAtLogin()` now resolves `false` on a permanent failure instead of rejecting an unhandled `void`ed promise. Transient failures stay silent (proactive refresh still retries). Also trims the verbose unified-lifecycle comments flagged in review. - **Breaking**: None. Flag OFF is byte-for-byte the current cascade. ## Review Focus - **Single token, no fallback under the flag.** Tests assert `getAuthHeader`/`getAuthToken` return only the unified token and never call `getIdToken` or the API-key store; they return `null`/`undefined` (not a fallback) when the token is unminted. - **Surfacing, not recovery.** This PR makes the terminal state *visible* (toast); the existing router auth-guard still redirects to login on the next navigation. Active recovery (automatic re-mint on 401) stays in the deferred safety-net PR so the toast is never a dead-end "please re-login" with the fix one PR away. - **Flag-OFF parity.** The full existing cascade suite runs with `unifiedCloudAuthEnabled = false` (the `beforeEach` default) and stays green. ## Deferred (intentional) - **`acceptInvite` is left unchanged — still Firebase-authed.** It is the one cloud call that intentionally keeps the raw Firebase token, because the invite is accepted *before* the user is a member of the target workspace. Promoting it to the unified Cloud JWT first needs a quick check that `POST /invites/:token/accept` accepts a personal-scoped Cloud JWT for a not-yet-member; deferred until that is verified. `getFirebaseAuthHeader` / `getFirebaseAuthHeaderOrThrow` stay defined (their removal belongs to the later cleanup ticket, FE-951). No `workspaceApi.ts` change in this PR. - **The reactive 401 re-mint + retry safety net is a follow-up.** A clean place to intercept a `401` and re-mint once does not exist yet: cloud requests use raw `fetch` (`/customers/*`, `/auth/token`) plus several independent axios clients (`workspaceApi`, `customerEventsService`, registry, manager), with no shared response interceptor. PR2's `remintUnifiedOnce()` primitive is ready, and the proactive buffer-based refresh (`refreshUnified`) already covers the common token-*expiry* case, so this cross-cutting safety net (plus deciding whether the surfacing toast escalates to a guided re-login CTA once remint exists) lands in its own focused PR before any production rollout. Note this is orthogonal to the surfacing above: proactive refresh prevents expiry; it cannot prevent *revocation*, which is exactly what triggers the now-surfaced permanent-error path. ## Tests - Extended `authTokenPriority.test.ts`: flag-ON `getAuthHeader` returns only the unified JWT (Firebase + API-key + workspace untouched) and `null` when unminted; flag-ON `getAuthToken` returns the unified JWT (not Firebase) and `undefined` when unminted. Existing cascade tests prove flag-OFF parity. - Added to `useWorkspaceAuth.test.ts` (red-green + regression lock): a permanent refresh error toasts the **correct i18n key for each of the four permanent codes** (`it.for` over 403/404/401 + a lost-Firebase-token `NOT_AUTHENTICATED` case) and clears the slot; a permanent login-mint error toasts and resolves `false`. Negative guards prove the surfacing is **error-only and flag-scoped**: a transient (5xx) refresh does **not** toast and keeps the slot, a **successful** re-mint does not toast, and the unified lifecycle **never toasts when the flag is OFF** (even against a rejecting backend). ## Red-Green Verification | Commit | CI | Purpose | |--------|-----|---------| | `test: cover permanent unified-auth error surfacing` | 🔴 Red ([run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/27455949404)) | Proves the tests catch the silent-failure gap | | `fix: surface permanent unified-auth errors instead of failing silently` | 🟢 Green ([run](https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/27456200098)) | Proves the surfacing resolves it | Part of FE-950 (single Cloud-JWT provider at login, Phase 1).