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 you need to know to 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.

The token endpoint should be placed inside of your system.

Token endpoint diagram.

# The JSON Web Tokens specification

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.

The 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 aud (audience), exp (expiration time), sub (subject) or iat (issued at). In CKEditor Cloud Services we use aud for identifying environments, iat for checking if the token has not expired and sub for identifying users.

# Signature

Unlike the header and the payload, the signature is not a JSON object but instead 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 verification that the token has not been tampered with:

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

CKEditor Cloud Services supports signatures created by the 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 again 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 the form of a string.

An example response might look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiSm9obiJ9.3MOd-0xmppWAX_86vMPjQ0PTKAniCtr762UTM5WuGa8

# Token

The token for the cloud services should specify Environment ID as the aud property and it needs to be signed with Access key. Both can be found in the CKEditor Ecosystem customer dashboard - refer to the documentation for environments management. 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 sub claim, which identifies the user. You can also put a user object to define other properties like name, email or avatar.

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.

# Payload properties

The following properties must be included in the payload:

  • aud – The environment ID.
  • iat – “Issued at”. Make sure that iat is present and contains a correct time stated in seconds. Some JWT implementations do not include it by default. Sometimes the system time may also be invalid, causing weird issues with tokens (see e.g. Docker for Mac time drift).
  • auth – If you plan to use collaboration, the auth.collaboration roles inside are required. If you just use Easy Image, you may skip it.

The properties that are optional:

  • sub – The user ID.
  • user – User information. Providing name and email is recommended.
  • exp – Token expiration time. Identifies the expiration time after which the JWT will not be accepted. Cloud Services only accept tokens no older than 24 hours. This field can be used to shorten the token validity time.

# Example token payload

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

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

# Tools

We highly recommend using the libraries listed on jwt.io to create the token. You can also check our token endpoint implementation examples in the following languages:

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

# Most common issues

# Invalid token

CKEditorError: cloud-services-internal-error: Invalid token. Error Trace id: <TRACE ID>. Read more: <https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-cloud-services-internal-error> {"errors":[]}

Check if the token was created in accordance with our standards. You can also refer to our token endpoint examples.
Please also ensure that you are using a correct and valid access key which is assigned to the right environment.
You can validate the token using the jwt.io debugger.

# You don’t have enough permissions to access this resource

CKEditorError: cloud-services-internal-error: You don't have enough permissions to access this resource. Error Trace id: <TRACE ID>. Read more: <https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-cloud-services-internal-error> {"errors":[]}

Please ensure that your token endpoint includes the right roles for the requested resource.
You can read more about the role mechanism in the Roles guide.