Real-Time Collaboration (RTC) JWT Authentication Setup

TinyMCE’s Real-time Collaboration (RTC) plugin will be retired and deactivated on December 31, 2023, and is no longer available for purchase.

Introduction

Real-Time Collaboration (RTC) requires setting up JSON Web Token (JWT) authentication. This is to ensure that only authenticated users will be able to access and collaborate on documents.

A JSON Web Token (JWT) endpoint is a service for generating and providing authorization tokens to users. These tokens are used to verify that submitted content was sent by an authorized user and to prevent submissions by unauthorized collaborators.

JWT is a standard authorization solution for web services and is documented in detail at https://jwt.io/. This guide aims to show how to setup JWT authentication for RTC.

Setting up JWT authentication for Real-Time Collaboration

To set up JSON Web Token (JWT) authentication for TinyMCE Real-Time Collaboration (RTC):

  1. Add a public key to your Tiny Account.

  2. Set up a JSON Web Token (JWT) Provider endpoint.

  3. Configure TinyMCE to use the JWT endpoint.

The Real-Time Collaboration (RTC) Server requires a public key generated from the same private key that will be used on your JSON Web Token (JWT) provider endpoint. The public key(s) stored on the Real-Time Collaboration (RTC) Server are used to ensure that content is sent by authorized users.

There are two methods for generating and adding a public key to your API key:

  1. The secure key pair generator at Tiny Account - JWT Keys (recommended).

  2. Generate a key pair locally and add the public key to Tiny Account - JWT Keys.

Generate a key pair using the Tiny Account JWT Keys page

The Tiny Account - JWT Keys page provides a private/public key generator, providing a quick and secure way of generating the required keys. This generator will store a copy of the public key, and provide a downloadable file for both the public and private keys. Tiny does not store the private key and the key pair cannot be retrieved later.

Generate a key pair locally

When generating a key pair locally, use one of the supported algorithms. Real-Time Collaboration (RTC) does not support symmetrical encryption algorithms, such as HS256. Tiny recommends using the RS256 algorithm. The following algorithms are supported:

  • RS256

  • RS384

  • RS512

  • PS256

  • PS384

  • PS512

For instructions on generating a key pair locally, see: Creating a private/public key pair for Tiny Cloud.

Add a public key to the Tiny Cloud API key

Once a public key has been generated, add the public key to the Tiny Cloud API key at: Tiny Account - JWT Keys.

Set up a JSON Web Token (JWT) endpoint

A JSON Web Token (JWT) endpoint is a service for generating and providing authorization tokens to users. These tokens can then be used to verify that submitted content was sent by an authorized user and to prevent unauthorized access.

The following diagram shows how JWTs are used:

JSON Web Token Call Flow
Figure 1. JSON Web Token Call Flow

When a user opens Real-Time Collaboration (RTC):

  1. The Real-Time Collaboration (RTC) plugin requests a signed JWT on behalf of the user.

  2. If your JWT endpoint authorizes the user, your JWT endpoint will send a JWT to the Real-Time Collaboration (RTC) plugin, certifying the user.

  3. When the user makes a request (such as adding or deleting content), the JWT will be sent with the request to show that the user is authorized. This JWT is verified using the public key stored on the RTC Server.

  4. The RTC Server sends the verified content to collaborating editors.

JWT endpoint requirements

A JSON Web Token (JWT) endpoint for Real-Time Collaboration (RTC) requires:

  • The endpoint or server accepts a JSON HTTP POST request.

  • User authentication - A method of verifying the user, and that they should have access to the document.

  • The JWTs are generated (signed) using the private key that pairs with the public key generated at (or provided to) Tiny Account - JWT Keys.

  • The endpoint or server produces a JSON response with the token. The RTC plugin will submit the token with requests to the RTC Server.

Required JWT claims for Real-Time Collaboration

JSON Web Tokens produced by the JWT endpoint must include the following claims:

Data Optional or required Type Description

sub

required

string or URI

The unique user ID string or URI.

exp

required

number

The unix timestamp for when the token expires.

The sub field is used to identify users to avoid sending sensitive or identity information to Tiny in plain text. By minimizing the information in JWT claims and relying on the client-side resolution of user IDs, no private data will be transmitted through the RTC server without encryption.

JWT endpoint examples

The following examples show a minimal JWT endpoint and how to configure TinyMCE to use them.

PHP token provider endpoint example

This example uses the Firebase JWT library provided through the Composer dependency manager.

$privateKey should be the private key that pairs with the public key generated at (or provided to) Tiny Account - JWT Keys.

jwt.php

<?php
require 'vendor/autoload.php';
use \Firebase\JWT\JWT;

header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");

$privateKey = <<<EOD
-----BEGIN PRIVATE KEY-----
....
-----END PRIVATE KEY-----
EOD;

// NOTE: Before you proceed with the TOKEN, verify your users session or access.

$payload = array(
  "sub" => "123", // unique user ID string
  "exp" => time() + 60 * 10 // 10 minute expiration
);

try {
  $token = JWT::encode($payload, $privateKey, 'RS256');
  http_response_code(200);
  header('Content-Type: application/json');
  echo json_encode(array("token" => $token));
} catch (Exception $e) {
  http_response_code(500);
  header('Content-Type: application/json');
  echo $e->getMessage();
}
?>

TinyMCE example using the jwt.php endpoint

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),
  rtc_token_provider: ({ documentId }) =>
    fetch('jwt.php', {
      method: 'POST',
      credentials: 'include',
      body: JSON.stringify({ documentId }),
    })
    .then((response) => response.json())
    .catch((error) => console.log('Failed to return a JWT\n' + error))
});

Node.js token provider endpoint example

This example shows how to set up a Node.js express handler that produces the tokens. It requires you to install the Express web framework and the jsonwebtoken Node module. For instructions on setting up a basic Node.js Express server and adding TinyMCE, see: Integrating TinyMCE into an Express JS App.

privateKey should be the private key that pairs with the public key generated at (or provided to) Tiny Account - JWT Keys.

/jwt

const express = require('express');
const jwt = require('jsonwebtoken');
const cors = require('cors');

const app = express();
app.use(cors());

const privateKey = `
-----BEGIN PRIVATE KEY-----
....
-----END PRIVATE KEY-----
`;

app.post('/jwt', (req, res) => {
  // NOTE: Before you proceed with the TOKEN, verify your users' session or access.
  const payload = {
    sub: '123', // Unique user ID string
    exp: Math.floor(Date.now() / 1000) + (60 * 10) // 10 minutes expiration
  };

  try {
    const token = jwt.sign(payload, privateKey, { algorithm: 'RS256'});
    res.set('content-type', 'application/json');
    res.status(200);
    res.send(JSON.stringify({
      token: token
    }));
  } catch (e) {
    res.status(500);
    res.send(e.message);
  }
});

app.listen(3000);

TinyMCE example using the /jwt endpoint

tinymce.init({
  selector: 'textarea', // change this value according to your HTML
  plugins: 'rtc',
  rtc_document_id: 'unique-document-id',
  rtc_encryption_provider: () => Promise.resolve({ key: 'a secret key' }),

  rtc_token_provider: ({ documentId }) =>
    fetch('/jwt', {
      method: 'POST',
      credentials: 'include',
      body: JSON.stringify({ documentId }),
    })
    .then((response) => response.json())
    .catch((error) => console.log('Failed to return a JWT\n' + error))
});