Collaboration Permissions

InkLayer collaboration permissions control mutations in the annotator: who can create annotations and replies, and who can move, resize, edit, change status, or delete existing content. React and Vue share the same permission model; only the component binding syntax differs.

InkLayer does not provide login or authentication. user must come from your application. Permission decisions rely on a stable, trusted user.id; no separate role prop is required.

Quick Start

React

import { PdfAnnotator } from 'inklayer-react'
import type { AnnotationPermissions, User } from 'inklayer-react'

const currentUser: User = { id: 'alice', name: 'Alice' }

const permissions: AnnotationPermissions = {
  mode: 'owner-only',
  can: ({ currentUser }) =>
    currentUser?.id === 'admin' ? true : undefined,
}

export function ReviewDocument() {
  return (
    <PdfAnnotator
      url="/document.pdf"
      user={currentUser}
      annotationPermissions={permissions}
      defaultShowAnnotationAuthorLabels
      layoutStyle={{ height: '100vh' }}
    />
  )
}

Vue

<script setup lang="ts">
import { PdfAnnotator } from 'inklayer-vue'
import type { AnnotationPermissions, User } from 'inklayer-vue'

const currentUser: User = { id: 'alice', name: 'Alice' }

const permissions: AnnotationPermissions = {
  mode: 'owner-only',
  can: ({ currentUser }) =>
    currentUser?.id === 'admin' ? true : undefined,
}
</script>

<template>
  <PdfAnnotator
    url="/document.pdf"
    :user="currentUser"
    :annotation-permissions="permissions"
    default-show-annotation-author-labels
    :layout-style="{ height: '100vh' }"
  />
</template>

Current User and Ownership

user represents the application user currently operating the annotator:

interface User {
  id: string
  name: string
}
  • id drives permission decisions and must remain stable across user switches, reloads, and restored documents.
  • name is display metadata. It may change and should not be used for authorization.
  • When id is empty or equals the default value 'null', owner-only treats the current user as unauthenticated and denies every mutation.
  • Changing user immediately applies subsequent permission checks to the new current user.

Display Annotation Author Labels

defaultShowAnnotationAuthorLabels determines whether all annotation author labels are visible when the annotator initializes. It defaults to false in both React and Vue:

<PdfAnnotator defaultShowAnnotationAuthorLabels />
<PdfAnnotator default-show-annotation-author-labels />
  • false only keeps all labels collapsed by default; selecting an annotation still reveals that annotation’s author.
  • Users can use the author-label button in the annotation toolbar to keep all labels visible or hidden.
  • Hold Command on macOS or Alt on Windows/Linux to reveal all author labels temporarily. Releasing the key restores the previous state.
  • This prop sets the initial state; it is not a controlled switch for subsequent visibility changes.
  • Labels use the author display information stored with each annotation. They help identify ownership but do not change annotationPermissions; permission checks still rely on stable author IDs and the current user.id.

Permission Modes

annotationPermissions.mode supports two modes:

ModeDefault behavior
unrestrictedDefault. Preserves the compatible behavior in which all annotation and reply mutations are allowed.
owner-onlyValid users can create annotations and replies; only authors can manage their own content.

Omitting annotationPermissions is equivalent to:

{ mode: 'unrestricted' }

owner-only Action Matrix

ActionAnnotation authorOther valid userNo valid user
Create an annotationAllowedAllowedDenied
Reply to an annotationAllowedAllowedDenied
Move or resize an annotationAllowedDeniedDenied
Edit an annotationAllowedDeniedDenied
Change annotation statusAllowedDeniedDenied
Delete an annotationAllowedDeniedDenied
Edit or delete a replyReply author onlyDeniedDenied

The permission system restricts mutations only. Users can still view, select, and inspect annotations they cannot modify.

Override Defaults with can(request)

can is an optional synchronous resolver. InkLayer first calculates defaultAllowed from the selected mode, then calls can:

interface AnnotationPermissions {
  mode?: 'unrestricted' | 'owner-only'
  can?: (request: AnnotationPermissionRequest) => boolean | undefined
}

Return values:

Return valueResult
trueForce allow the current action
falseForce deny the current action
undefinedKeep the mode’s defaultAllowed result

The request contains:

FieldDescription
actionThe requested mutation
currentUserCurrent user, or null when unavailable
annotationCurrent annotation; may be absent for create actions
commentCurrent reply; absent for non-reply actions
defaultAllowedThe result already calculated by the current mode

Supported Actions

type AnnotationPermissionAction =
  | 'annotation.create'
  | 'annotation.transform'
  | 'annotation.edit'
  | 'annotation.delete'
  | 'annotation.comment'
  | 'annotation.change-status'
  | 'comment.edit'
  | 'comment.delete'

Administrator Override

const permissions: AnnotationPermissions = {
  mode: 'owner-only',
  can: ({ currentUser }) =>
    currentUser?.id === 'admin' ? true : undefined,
}

The administrator returns true; everyone else returns undefined and continues using the owner-only ownership rules.

Fully Read-Only Annotator

React:

<PdfAnnotator annotationPermissions={{ can: () => false }} />

Vue:

<PdfAnnotator :annotation-permissions="{ can: () => false }" />

Users can still select and inspect annotations, but every mutation is denied.

Deny Deletion

const permissions: AnnotationPermissions = {
  mode: 'owner-only',
  can: ({ action }) =>
    action === 'annotation.delete' ? false : undefined,
}

Lock a Document After Review

const permissions: AnnotationPermissions = {
  mode: 'owner-only',
  can: ({ currentUser }) => {
    if (documentLocked) return false
    if (currentUser?.id === 'admin') return true
    return undefined
  },
}

documentLocked comes from your application’s document or approval state. In this order, a locked document also blocks administrators. Check the administrator first if administrators should bypass the lock.

can must return synchronously. Load roles, document state, and approval data first, then provide them through a closure. Do not return a Promise. If the resolver throws, InkLayer denies the current action to avoid accidentally granting access.

Preserve Ownership When Saving

owner-only depends on persisted author IDs. Do not remove or rewrite these fields when saving and restoring annotations:

  • Annotation meta.authorId
  • Reply user

meta.authorId may be a string ID or a user object:

annotation.meta.authorId = {
  id: 'alice',
  name: 'Alice',
}

If legacy data has no author information, InkLayer cannot establish ownership and those annotations cannot be edited in owner-only mode. During migration, populate authorship with real, stable IDs from your application.

Client Permissions Are Not a Security Boundary

annotationPermissions controls InkLayer UI and local browser mutations. It does not replace server-side authorization.

Your backend must still verify:

  • Whether the current session may access the document
  • Whether the current user owns an annotation being saved or deleted
  • Whether administrator or approval privileges are valid
  • Whether the submitted authorId may be written

Never trust permission results, user IDs, or authorship submitted by the client without server validation.

Troubleshooting

Everything is disabled after enabling owner-only

Make sure user.id is non-empty and does not equal 'null'.

The current user cannot edit a restored annotation

Make sure persisted data retains annotation.meta.authorId and that it exactly matches the current user.id.

The administrator override does not work

Make sure can returns the boolean true and that the administrator ID matches currentUser.id. Returning undefined preserves the mode’s default decision.

Can can request permission data from the backend?

No. can is synchronous. Fetch business permissions first, then create or update annotationPermissions.