guideToken endpoint

# Overview

To connect CKEditor 5, CKEditor 4 or Letters with CKEditor Cloud Services, you need to create your own token endpoint. This guide will explain a few principles that let you create it.

# How CKEditor Cloud Services uses tokens

To authenticate users, CKEditor Cloud Services uses tokens. The purpose of tokens is to inform the cloud services that the user has access to resources and to which environment the user should connect to. The authenticity of tokens is provided by a digital signature.

The token endpoint is where the client (using CKEditor or Letters) makes a request to get the token. It should return the token only if the user proves their identity.

# A few words about JSON Web Tokens

CKEditor Cloud Services tokens are represented as JSON Web Tokens (JWT). JWT is an open standard for transmitting digitally signed information. Using it ensures that the data comes from a trusted source.

Tokens consist of three parts:

  • header
  • payload
  • signature

Each part is encoded in base64url and separated by periods:

header.payload.signature

An example token can look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiSm9obiJ9.3MOd-0xmppWAX_86vMPjQ0PTKAniCtr762UTM5WuGa8

The header indicates that it is a JWT and defines the hashing algorithm for calculating the signature. It is simply a JSON object with the typ and alg properties, where typ always contains the JWT value and alg contains the name of the algorithm used.

# Payload

The payload is a JSON object with the claims. In CKEditor Cloud Services it is mostly used for specifying the environment, user data and permissions.

The JWT standard specifies some predefined claims like iss (issuer), exp (expiration time), sub (subject), aud (audience) or iat (issued at). In CKEditor Cloud Services we use iss for identifying environments and iat for checking if the token has not expired.

# Signature

Unlike the header and payload, the signature is not a JSON object but raw bytes produced by the cryptographic algorithm.

The signature is calculated for the header merged with the payload. It enables the authentication of data and verifying that the token has not been tampered with:

signature = HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload),
    secret
)

CKEditor Cloud Services supports signatures created by HS256, HS384 and HS512 algorithms.

Never disclose the secret key because it will allow to forge tokens.

# Requests to the token endpoint

The token for CKEditor Cloud Services is requested by the CKEditor 4 and CKEditor 5 rich-text editor features that require the cloud services to work, such as Easy Image and real-time collaborative editing.

# Simple usage

# CKEditor 5

The easiest way to request the token endpoint is to set the config.cloudServices.tokenUrl option to the endpoint. It will be requested from the editor by the Cloud Services plugin with a simple HTTP request at the initialization stage and then after every hour.

ClassicEditor.create( element, {
    // ...
    cloudServices: {
        tokenUrl: 'https://example.com/cs-token-endpoint'
    }
} );

If you need more control over the request, refer to the Customizing the token request method section below.

# CKEditor 4

The easiest way to request the token endpoint is to set the cloudServices_tokenUrl option to the endpoint. It will be requested from the editor with a simple HTTP request at the initialization stage and then after every hour.

CKEDITOR.replace( 'editor', {
    cloudServices_tokenUrl: 'https://example.com/cs-token-endpoint',
} );

You can find more information about the integration with CKEditor 4 in the Easy Image Integration guide.

# Customizing the token request method

# CKEditor 5

If you would like to change the default method of requesting the token from the server (described in the previous section), you can set the config.cloudServices.tokenUrl option to a callback. This callback will be used by the Cloud Services plugin to retrieve the token. It allows specifying exact HTTP request parameters for the token endpoint that should return the token value. The callback needs to return a promise that will be resolved with the token value or rejected with an error.

Note that the editor will not be ready to use until the first token is obtained from the token endpoint. If an error occurs during the initial request, the editor will not start correctly.

An example below presents setting a custom callback to the tokenUrl configuration that adds a custom header to the request:

const tokenUrl = 'https://example.com/cs-token-endpoint';

ClassicEditor.create( element, {
    // ...
    cloudServices: {
        tokenUrl: () => {
            return new Promise( ( resolve, reject ) => {
                const xhr = new XMLHttpRequest();

                xhr.open( 'GET', tokenUrl );

                xhr.addEventListener( 'load', () => {
                    const statusCode = xhr.status;
                    const xhrResponse = xhr.response;

                    if ( statusCode < 200 || statusCode > 299 ) {
                        return reject( new Error( 'Cannot download a new token!' ) );
                    }

                    return resolve( xhrResponse );
                } );

                xhr.addEventListener( 'error', () => reject( new Error( 'Network error' ) ) );
                xhr.addEventListener( 'abort', () => reject( new Error( 'Abort' ) ) );

                xhr.setRequestHeader( customHeader, customValue );

                xhr.send();
            } );
        }
    }
} );

The current token value is accessible via the CloudServices plugin.

const cloudServices = editor.plugins.get( 'CloudServices' );
const tokenValue = cloudServices.token.value;

# Responses from the token endpoint

The endpoint should respond with a generated token in a form of a string.

The example response might look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiSm9obiJ9.3MOd-0xmppWAX_86vMPjQ0PTKAniCtr762UTM5WuGa8

# Token

The token for the cloud services should specify Environment ID as the iss property and it needs to be signed with Secret key as the secret key. Both can be found in the CKEditor Ecosystem customer dashboard. Optionally, the token can contain the user data. Otherwise CKEditor Cloud Services will treat the user as an anonymous user.

# User

If the user is not anonymous, the payload should contain the user object with an id property. You can also put there other data like name or email.

The user identifier should be unique within the environment scope, so you should not use the same environmentId in two applications because it can cause user ID collisions.

# Roles

To define permissions to resources, the token payload should include the auth object with specified user roles.

{
    "auth": {
        "collaboration": {
            "*": {
                "role": "writer"
            }
        }
    }
}

More information about roles can be found in the Roles guide.

# Required payload properties

The following properties must be included in the payload:

  • iss – The environment ID.
  • iat – “Issued at”. Make sure that iat is present and contains correct time. Some JWT implementations do not include it by default. Also sometimes the system time may be invalid, causing weird issues with tokens (see e.g. Docker for Mac time drift).
  • user – User information. Only id is required, but providing name and email is recommended.
  • auth – If you plan to use collaboration, auth.collaboration roles inside are required. If you just use Easy Image, you may skip it.

# Example token payload

The example below presents a complete token payload with access to editing all documents in the environment:

{
    "iss": "NQoFK1NLVelFWOBQtQ8A",
    "iat": 1511963669,
    "user": {
        "id": "exampleuser",
        "email": "example@cksource.com",
        "name": "A User",
        "avatar": "http://example.com/avatars/john.png"
    },
    "auth": {
        "collaboration": {
            "*": {
                "role": "writer"
            }
        }
    }
}

# Tools

To create the token we highly recommend using libraries listed on jwt.io. You can also look at a sample token endpoint in Node.js, a token endpoint in PHP or a token endpoint in ASP.NET.

If you are having troubles with creating the token endpoint in another server-side language, or if you have any other suggestions regarding the documentation, please contact us.