We’re integrating self-hosted ONLYOFFICE Docs (Community Edition) into our own product via the standard DocsAPI.DocEditor() JS API, with JWT enabled on the Document Server. We’re hitting a reproducible bug where the editor’s own client code appears to replace a correctly-signed JWT with a fixed, unrelated placeholder string before sending it in the co-authoring websocket’s auth message — causing every session to fail with:
“The document security token is not correctly formed. Please contact your Document Server administrator.”
Environment
- Self-hosted via Docker, tested on both
onlyoffice/documentserver:10.0.0andonlyoffice/documentserver:8.3.3— identical failure on both, so this doesn’t appear to be version-specific. JWT_ENABLED=true, a single shared secret used throughout (confirmed identical acrossservices.CoAuthoring.secret.browser/inbox/outbox/sessioninlocal.jsonand the secret our backend signs with).- Editor is embedded cross-origin: our app (e.g.
https://app.ourdomain.com) loads the Document Server’sapi.jsfrom a separate origin (https://docs.ourdomain.com) and callsDocsAPI.DocEditor(containerId, config)with a server-signed config.
What we verified before concluding this is client-side
- The config our backend sends over HTTP includes a correctly-signed JWT (
config.token) — we independently decoded and verified it against the shared secret; it’s valid. - We set a breakpoint in our own code at the exact line calling
DocsAPI.DocEditor(containerId, config)and confirmedconfig.tokenstill holds the correct, real JWT at that point. - Despite that, the outgoing websocket message to
/<version>/doc/<key>/c/(Engine.IO packet type40, Socket.IOauth) contains atokenfield with a fixed placeholder string — not our JWT, not empty/undefined, a different fixed value every time, with every other field in that same message (docid,documentCallbackUrl,user.id, etc.) correctly matching what we sent. - We ruled out anything on our end: reproduces in a fresh Incognito profile (no extensions), with no DevTools local overrides, after unregistering the Document Server’s own service worker and clearing site data, with our own frontend’s third-party SDKs (Sentry, PostHog) disabled, and with
editorConfig.coEditingset to{mode: "strict", change: false}(no change). - Disabling
services.CoAuthoring.token.enable.browserinlocal.json(i.e. turning off this specific JWT check) makes the document open and render correctly — so the rest of the flow (file fetch, conversion, rendering) works fine; the issue is isolated to this one check/connection.
Config shape used (values genericized, structure exact):
{
"type": "embedded",
"width": "100%",
"height": "100%",
"document": {
"key": "doc-<uuid-hex>-base",
"title": "example.xlsx",
"url": "https://<presigned-s3-url>",
"fileType": "xlsx"
},
"documentType": "cell",
"editorConfig": {
"mode": "view",
"callbackUrl": "https://<our-callback-endpoint>",
"user": { "id": "<uuid>", "name": "<name>" },
"customization": { "forcesave": true }
},
"token": "<valid JWT signed with the shared secret, verified independently>"
}
Questions
- Is this a known issue with the co-authoring/connection JWT check specifically (as opposed to the initial editor-config token, which validates fine)?
- Is there an additional, undocumented config requirement for this specific websocket handshake that differs from the documented
config.tokencontract? - Happy to provide a full HAR capture, the exact
docservicelog lines (checkJwtForConnection refused: ...), or a minimal standalone repro if useful.
Thanks for any guidance