Client Authentication

JWT is signed with a private key on your server and verified with the public key on PSPDFKit Server

PSPDFKit Server uses the JSON Web Token format for authentication. Your backend signs JWT tokens asserting that the holder of such a token is allowed access to a given document. It then passes them to your web app that uses PSPDFKit for Web. Your app then uses its token when initializing PSPDFKit for Web, where it is passed to PSPDFKit Server to prove it has access to the claimed document.

You can generate a token with one of the many open source libraries that are available and listed on

Things to keep in mind when generating a token:

  • It has to include the standard claim "exp", which sets the deadline for the validity of the token. This needs to be a non-negative number as a Unix "Seconds Since the Epoch" timestamp.

  • It has to include the user-defined claims "document_id" and "permissions".

    • "document_id" is the identificator of a document that has been previously uploaded to the server. This field has to be a string value.
    • "permissions" is a list of permission names that define which features will be accessible to the holder of this token. See the Permissions section below for more details.
  • It may include the "user_id" claim, which will be stored on any annotation created, updated, or deleted by the user.

  • It may include the "layer" claim, which defines the Instant layer that all changes will be persisted to.

  • It has to be signed using an asymmetric cryptographic algorithm. PSPDFKit Server supports the algorithms RS256, RS512, ES256, and ES512. See RFC 7518 for details about specific algorithms.


Available permissions:

  • "read-document" — required for viewing a document and its annotations. Without this permission, it won't be possible for the user to load the document and perform any operations on it.

  • "write" — required for creating, updating, and deleting annotations in a document. If this permission is not present, PSPDFKit for Web will always be in read-only mode.

  • "download" — required for downloading and printing a document's PDF file.

  • "cover-image" — required for accessing the /documents/cover endpoint.

Any combination of the above permissions can be included in the permissions list when generating a JWT. Apart from that, the permissions field of a JWT may have one of the following special values:

  • "all-2017.3" will enable all permissions available in the release 2017.3, namely "read-document", "write", and "download".

  • "all-2017.9" will enable all permissions available in the release 2017.9, namely "read-document", "write", "download", and "cover-image".

  • "all" will enable all permissions supported by the running version of the server.

Generating Tokens

The following example shows the creation of a JWT in a Node/Express app using the jsonwebtoken library. It assumes you chose the RS256 algorithm and have created a pair of private and public keys.

To create the key, you can use ssh-keygen:

ssh-keygen -t rsa -b 4096 -f jwtRS256.key
# Enter your passphrase

# Get the public key in PEM format:
openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256_pub.pem

Now use the private key to sign the tokens you hand out to the clients and add the public key to the server's configuration so that the server will be able to validate the tokens' signatures.

If you want to quickly test PSPDFKit for Web with your application, you can also use the key from our example apps (passphrase: secret). Make sure to change to a self-generated key before going into production.

// index.js

const express = require('express');
const router = express.Router();
const fs = require('fs');
const jwt = require('jsonwebtoken');
const key = fs.readFileSync('./jwt.pem');
const permissions = ['read-document', 'write'];
const token = jwt.sign({ document_id: 'abc', permissions: permissions }, key, {
  algorithm: 'RS256',
  expiresIn: 60 * 60 // 1hr, this will set the `exp` claim for us.

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { token: token });

module.exports = router;