Verentis

WOPI integration

WOPI integration with Collabora

Build a signed document-editor app using platform-owned WOPI, explicit installation consent and the workspace SDK bridge.

Verentis Collaboration hosts WOPI on behalf of installed editor applications. Your app supplies an HTTPS editor wrapper and uses Collabora Online / CODE to display and edit documents. The platform owns document authorization, WOPI credentials, locks and persistence; you do not build another WOPI backend.

A WOPI-only app does not need a publisher OAuth client ID or client secret. It needs an actively signed package, approved installation consent and a trusted editor deployment. A signature establishes package provenance, not unrestricted runtime access to a workspace.

Supported integration

This guide covers Collabora and the existing-file launch flow. It does not promise Microsoft 365 for the web compatibility, anonymous editing, arbitrary discovery servers or the entire WOPI protocol surface. For this integration, declare view and/or edit; a format must also be supported by the configured CODE discovery document and the user's current permissions.

Who owns what

OwnerResponsibilities
App publisherSign the package; declare wrapper/operator origins, formats and capabilities; implement the wrapper and SDK lifecycle.
Editor operatorRun CODE over trusted HTTPS, retain its RSA proof key, allow the platform WOPI origin, and protect document content and logs. The publisher may also be the operator.
Platform operatorApprove/configure trusted CODE discovery, operate Collaboration and provision its internal credentials, signing keys and private TLS.
Workspace installerReview the exact signed candidate and consent to external document processing and requested operations.
Workspace userOpen documents using their current file permissions; installation consent does not grant the user new file access.

A new CODE operator origin needs platform discovery configuration. An app manifest cannot make the platform fetch arbitrary URLs merely by declaring them. Multiple approved wrappers can use the same trusted CODE operator.

1. Declare the editor

The following application manifest illustrates a DOCX-only wrapper. Replace the publisher and origins with your own, then add the packaging configuration described in Publish an application.

api-version: verentis.io/v1
kind: Application
metadata:
  name: document-editor
  display-name: Document Editor
  version: 0.1.0
  author: Acme
spec:
  entry: https://editor.example.com
  scope: global
  capabilities: [wopi]
  permissions: []
  mime-types:
    - pattern: application/vnd.openxmlformats-officedocument.wordprocessingml.document
      mode: edit
      priority: 150
  wopi:
    editor-origin: https://editor.example.com
    operator-origin: https://code.example.com
    actions: [view, edit]
    maximum-file-bytes: 52428800
    idle-session-minutes: 30
    files:
      - mime-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document
        extension: .docx
        editable: true
  sandbox:
    allow-scripts: true
    allow-same-origin: true
    allow-forms: true

permissions: [] is intentional for this WOPI-only example. The host brokers Collaboration operations using the user's authority; the editor does not need an ordinary delegated file-write token. If your app also calls ordinary platform APIs, declare and obtain their permissions independently.

The wrapper origin and CODE operator origin are different roles. spec.entry must belong to wopi.editor-origin; operator-origin identifies CODE, not the platform API. Neither field is the WOPI callback URL. See the complete field reference.

Use your publisher's active signing key and the normal signed publication workflow. Apply environment overrides before signing: changing origins or other declaration values afterward does not produce an authorized candidate.

Install the published package in a test workspace and review its WOPI consent. The review identifies the publisher, wrapper/operator origins, supported actions and files, limits and optional rename/delete operations. It also discloses that an external editor processes document content. Publication/moderation and workspace consent are separate decisions.

An unsigned package, a loose manifest without verified installation provenance, or a pending/revoked installation is not a WOPI launch authorization. See Installation & consent.

3. Receive the platform launch

Open a matching document from the workspace, rather than navigating directly to the wrapper. The host obtains a scoped embed ticket for the approved wrapper; the wrapper must validate framing through Collaboration. It then initializes the SDK and requests the document launch:

import { createVerentisClient } from '@verentis/sdk'

const client = createVerentisClient()
await client.whenReady()

if (client.context.surface !== 'file' ||
    !client.context.installation?.wopiEnabled ||
    !client.bridge) {
  throw new Error('An approved workspace WOPI file launch is required.')
}

const launch = await client.bridge.requestWopiLaunch()

The launch contains the resolved editor action URL, editorOrigin, wopiSource, scoped accessToken, epoch-millisecond accessTokenTtl, document identity and read-only state. Use these values, not a token or document identifier supplied by an arbitrary URL or another iframe. Treat the TTL as an absolute expiry, not a duration to add to the current time.

Submit access_token and access_token_ttl as form fields to the validated launch.action, targeting the CODE iframe. The resolved action already carries the WOPISrc. Keep credentials out of browser storage, diagnostic output, analytics and manually constructed navigation URLs. Validate both event.origin and event.source for editor messages, and use an exact targetOrigin.

This snippet is not a complete framing implementation. The Libre Office repository is the reference: apps/editor contains the wrapper's embed/frame authorization and CODE integration; manifests/office.app.yaml contains its signed declaration. The package name is libre-office; internal source and workload names may still say office.

4. Save with durable acknowledgement

CODE performs file and lock callbacks against the platform's /wopi/files/... endpoints. The wrapper coordinates the user interface through the SDK:

  1. Track a monotonically increasing local edit generation and mark the document dirty.
  2. Call bridge.requestWopiSave(generation) to obtain a checkpoint and correlation.
  3. Send CODE Action_Save with that correlation in Values.ExtendedData.
  4. Poll bridge.getWopiStatus() and correlate the platform receipt with the requested checkpoint and the editor's save response.
  5. Clear dirty state only when the receipt identifies a durable revision for the requested save and no newer local edit invalidates that acknowledgement.

requestWopiSave does not upload the document. CODE's Action_Save_Resp alone, or an unrelated revision change from another participant, is not proof that your current edits were persisted. On timeout or unavailable status, preserve the editor and show that changes remain unverified; do not report success or close it.

Save As uses WOPI PutRelativeFile. When status reports continuationAvailable, call bridge.requestWopiContinuation() to rebind controls to the server-selected target. The continuation contains target identity, not a new platform OAuth token or permission to select an arbitrary document. Follow the reference wrapper's transition handling; do not keep saving against the old source.

Call bridge.closeWopiSession() when explicitly closing a settled session. It releases participation; it is not a substitute for saving.

5. Exercise the real integration

Use an isolated local platform and CODE deployment. Exercise signed installation, consent, open, edit, correlated save and fresh reopen. Also check read-only users, two participants, Save As, optional operations, expired credentials and installation revocation.

Package validation and mocked editor messages do not establish that CODE can reach the gateway, verify TLS, produce valid proof signatures or persist edits. Preserve unsaved content when authority expires or is revoked. Do not design a client around a promise that an in-flight write is cancelled at the exact instant an idle deadline passes.

Troubleshooting

SymptomCheck
Wrapper refuses direct navigationLaunch through an installed app in the workspace; embed authorization is required.
No WOPI launch or consent deniedExact signed candidate, active signer, installation approval and current document rights.
Unsupported file/actionBoth MIME and extension declaration, editable, requested action and CODE discovery support.
Proof rejection or CODE cannot openTrusted operator discovery, stable CODE proof key, clock, callback origin and TLS trust. Never disable proof checking.
Redirect to *-api-service:8443Platform routing configuration: public HTTPS redirects belong at ingress, not the private API listeners.
Save response but no matching receiptCorrelation, newer local edits, live status and persistence outcome. Keep the editor open.

WOPI credentials are valid only for WOPI. They cannot authenticate ordinary platform API calls. See Authentication for the separate OAuth and SDK-token paths.