Skip to content

Liveblocks authEndpoint collaboration cannot authenticate: the worker's vendored Liveblocks client has no fetch polyfill #3862

Description

@ZacharyHampton

Version: superdoc@2.3.0 (@superdoc/docx-engine@0.2.0); the same resolution ships in 2.4.0-next (@superdoc/docx-engine@0.3.0-next.18).
Config: document.v2Collaboration = { providerType: 'liveblocks', documentId, authEndpoint, roomMode } (string authEndpoint — the server-auth mode).

What happens

  • The collaboration worker starts and completes its hello handshake.
  • No HTTP request is ever issued to the authEndpoint (and none to *.liveblocks.io).
  • After 10s the host fails the open: WorkerTransportError: v2 collaboration sync timed out after 10000ms (phase: 'worker.open_error', reason: 'open-session-failed').

So authEndpoint-based collaboration is unusable in the browser. publicApiKey avoids it but forfeits server-side authorization.

Root cause

Collaboration is worker-only, and the vendored @liveblocks/client lives in dist/assets/collaboration-worker-entry-*.js. Its auth manager resolves the fetcher as (verbatim from @liveblocks/core):

polyfills?.fetch ?? (typeof window === "undefined" ? undefined : window.fetch)

The worker adapter's createClient({ authEndpoint }) call site passes no polyfills, and in a DedicatedWorkerGlobalScope window is undefined — so a string authEndpoint throws Liveblocks' StopRetrying("…you need to provide a fetch polyfill.") inside the worker before any request. The host only ever sees the sync timeout.

Runnable proof against the shipped package (npm i superdoc@2.3.0, then node verify.mjs):

import { readFile, readdir } from 'node:fs/promises'
const dir = 'node_modules/@superdoc/docx-engine/dist/assets'
const entry = (await readdir(dir)).find((f) => f.startsWith('collaboration-worker-entry') && f.endsWith('.js'))
const src = await readFile(`${dir}/${entry}`, 'utf-8')
console.log('window-gated fetcher present:', src.includes("typeof window>'u'?void 0x0:window['fetch']"))
console.log('fetch-polyfill failure string present:', src.includes('fetch\\x20polyfill'))

Both print true on 2.3.0.

Fix (verified)

Either pass the polyfill at the call site:

createClient({ authEndpoint, polyfills: { fetch: globalThis.fetch.bind(globalThis) } })

or fall back to globalThis.fetch at the resolution site. We patched the shipped worker bundle with exactly that one-line change (typeof window>'u' ? globalThis.fetch?.bind(globalThis) : window.fetch) and authEndpoint collaboration authenticates, opens rooms, and syncs correctly in Chrome (verified end-to-end with a Next.js app and a standard Liveblocks ID-token auth endpoint — two clients converge, presence works).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions