EmbedPDF

Security & access

doc.security is your window into a document’s protection state and the caller’s authorization. Use it to gate UI, prompt for passwords, and unlock encrypted documents.

interface DocumentSecurityService {
  readonly current: DocumentSecurityState;
  readonly effectiveScope: ReadonlyArray<string>;
  readonly identity: IdentityClaims | null;
  readonly passwordPrompt: PasswordPrompt;
  unlock(input: { password: string; mode?: 'any' | 'owner' }): AbortablePromise<DocumentUnlockResult>;
}

Effective scope drives your UI#

effectiveScope is the caller’s expanded capability set — raw JWT scope plus PDF permission bits, run through the resolver’s implication rules. Gate feature visibility off this, not off raw token claims.

const canCopy = doc.security.effectiveScope.includes('doc.text.copy');
const canAnnotate = doc.security.effectiveScope.includes('doc.annotate.write');
 
if (!canCopy) hideCopyButton();

identity is the resolved caller identity (or null when anonymous): user_id, group_id, groups, display_name.

Inspecting protection state#

current is the structured security probe:

const { encryption, permissions, access } = doc.security.current;
 
encryption.state;            // 'unknown' | 'none' | 'encrypted' | 'unsupported'
encryption.requiresPassword; // boolean | null
permissions.openedAs;        // 'none' | 'user' | 'owner' | null
permissions.canUpgradeToOwner;
access.required;             // does the caller need to call /access first?
access.reasons;              // ('password' | 'cdn' | 'permissions-unknown')[]

Prompting for a password#

Rather than branching on raw flags, read passwordPrompt — the single source of truth for “should I ask the user for a password?”:

  • none — do nothing.
  • required — hard block; show a modal. hint labels the prompt.
  • optional — soft offer; show a banner (only ever to unlock owner rights).
const prompt = doc.security.passwordPrompt;
 
if (prompt.state === 'required') {
  const password = await askUser(prompt.hint); // hint: 'user' | 'owner' | null
  await doc.security.unlock({ password });
}

Unlocking#

unlock() submits a password and, on success, refreshes the security state and returns the updated access info.

const result = await doc.security.unlock({
  password: userInput,
  mode: 'owner', // optional: 'any' (default) or 'owner' to claim owner rights
});
 
result.security; // refreshed DocumentSecurityState
result.access;   // DocumentAccessInfo (scope, identity, CDN, pdf permissions)

When a document needs CDN access but no password (an access.reasons of just ‘cdn’), the engine establishes it transparently during open(), so CDN-signed URLs are ready before your first render. A password requirement is the case you handle explicitly with unlock().

Access vs. scope, briefly#

  • Encryption/permissions describe what the PDF allows (its security handler and permission bits).
  • Scope/identity describe what the caller is authorized to do, carried in the JWT and enforced by the server.

A request can be denied by either layer; both surface as typed EngineErrors (Forbidden, DocPasswordRequired, DocPasswordIncorrect) — see Async & errors.

Was this page helpful?

Your feedback goes directly to the documentation team.