ASP.NET Core

This guide describes the integration with an ASP.NET Core application. If you prefer to jump straight to the code, you can find the complete code of the application described in this guide on GitHub.

# Prerequisites

Before we start, please make sure that you have .NET SDK 7.0 installed in your system. If the dotnet tool is not available from the command line, please visit the .NET SDK download page first.

# Creating the application

As a base in our example, we will create a new project directory and an empty ASP.NET project using the following command:

dotnet new web --name AspdotnetCkbox

After the project is created, you can start the development server:

cd AspdotnetCkbox
dotnet run

The application will be available under a port listed in the console, for example, http://localhost:5063. The port can also be adjusted in the Properties/launchSettings.json file.

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:52536",
      "sslPort": 44341
    }
  },
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5063",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "https": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "https://localhost:7029;http://localhost:5063",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

# Creating the Token endpoint

CKBox, like other CKEditor Cloud Services, uses JWT tokens for authentication and authorization. For information on how to create access credentials, please refer to the Creating access credentials section in the Authentication guide.

Now that we have the required access credentials, namely: the environment ID and the access key, let’s create the token endpoint.

First, let’s install the System.IdentityModel.Tokens.Jwt package for creating JWT tokens.

dotnet add package System.IdentityModel.Tokens.Jwt

# Complete endpoint code

// Program.cs
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/token", () =>
{

    var environmentId = builder.Configuration.GetValue<string>("CKBoxEnvironmentId");
    var accessKey = builder.Configuration.GetValue<string>("CKBoxAccessKey");
    var securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(accessKey));

    var signingCredentials = new SigningCredentials(securityKey, "HS256");
    var header = new JwtHeader(signingCredentials);

    var dateTimeOffset = new DateTimeOffset(DateTime.UtcNow);

    var payload = new JwtPayload
    {
        { "aud", environmentId },
        { "iat", dateTimeOffset.ToUnixTimeSeconds() },
        { "sub", "user-123" },
        { "user", new Dictionary<string, string> {
            { "email", "joe.doe@example.com" },
            { "name", "Joe Doe" }
        } },
        { "auth", new Dictionary<string, object> {
            { "ckbox", new Dictionary<string, string> {
                { "role", "admin" }
            } }
        } }
    };

    var securityToken = new JwtSecurityToken(header, payload);
    var handler = new JwtSecurityTokenHandler();

    return handler.WriteToken(securityToken);
});

app.Run();

As you can see on the code listing above, the access credentials required to sign JWT tokens are obtained from the Configuration object. Thanks to this you can conveniently add them to the appsettings.json file:

{
  "CKBoxAccessKey": "REPLACE-WITH-ACCESS-KEY",
  "CKBoxEnvironmentId": "REPLACE-WITH-ENVIRONMENT-ID",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

# Adding CKBox script to the page

CKBox can be embedded in the application in multiple ways. For simplicity, in our example, we will use the CKBox script served from the CDN.

Examples in this guide will cover three popular scenarios of using CKBox:

  • CKBox integrated with CKEditor 5.
  • CKBox used as a file picker in dialog mode.
  • CKBox used as a full-page application.

First, let’s add Razor templating support to our application.

// Program.cs
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();

app.MapGet("/token", () =>
{

    var environmentId = builder.Configuration.GetValue<string>("CKBoxEnvironmentId");
    var accessKey = builder.Configuration.GetValue<string>("CKBoxAccessKey");
    var securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(accessKey));

    var signingCredentials = new SigningCredentials(securityKey, "HS256");
    var header = new JwtHeader(signingCredentials);

    var dateTimeOffset = new DateTimeOffset(DateTime.UtcNow);

    var payload = new JwtPayload
    {
        { "aud", environmentId },
        { "iat", dateTimeOffset.ToUnixTimeSeconds() },
        { "sub", "user-123" },
        { "user", new Dictionary<string, string> {
            { "email", "joe.doe@example.com" },
            { "name", "Joe Doe" }
        } },
        { "auth", new Dictionary<string, object> {
            { "ckbox", new Dictionary<string, string> {
                { "role", "admin" }
            } }
        } }
    };

    var securityToken = new JwtSecurityToken(header, payload);
    var handler = new JwtSecurityTokenHandler();

    return handler.WriteToken(securityToken);
});

app.MapRazorPages();
app.Run();

To avoid code repetition, let’s prepare a base layout that includes the CKBox script, which we will reuse in all three examples:

Pages/Shared/_Layout.cshtml

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>CKBox example</title>
    <script src="https://cdn.ckbox.io/ckbox/2.6.0/ckbox.js"></script>
    @await RenderSectionAsync("Head", required: false)
</head>
<body>

@RenderBody()

</body>
</html>

Now, let’s add pages for the three examples.

# CKBox with CKEditor 5

In this example, we will use the quickest and easiest way to run CKEditor 5 – served from the CDN. For more advanced integration scenarios, please refer to the CKEditor 5 documentation.

Let’s create the child view that extends the layout we have created in the previous step:

Pages/index.cshtml

@page

@{
    Layout = "Shared/_Layout";
    var tokenUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/token";
}

@section Head {
    <script type="importmap">
        {
            "imports": {
                "ckeditor5": "https://cdn.ckeditor.com/ckeditor5/42.0.0/ckeditor5.js",
                "ckeditor5/": "https://cdn.ckeditor.com/ckeditor5/42.0.0/"
            }
        }
    </script>
    <link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/42.0.0/ckeditor5.css" />
}
<div>
    <textarea id="editor">@Html.Raw("Hello world")</textarea>

    <script type="module">
        import {
            ClassicEditor,
            CKBox,
            Essentials,
            Bold,
            Italic,
            Font,
            Paragraph
        } from 'ckeditor5';

        ClassicEditor
            .create( document.querySelector( '#editor' ), {
                plugins: [ CKBox, Essentials, Bold, Italic, Font, Paragraph ],
                ckbox: {
                    tokenUrl: 'https://your.token.url',
                    theme: 'lark'
                },
                toolbar: [
                    'ckbox', '|', 'undo', 'redo', '|', 'bold', 'italic', '|',
                    'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor'
                ],
            } )
            .catch( error => {
                console.error( error );
            } );
    </script>
</div>

The above example includes a predefined CKEditor 5 build from the CDN, which includes the required CKBox plugin. Then, CKEditor 5 is configured to use CKBox by setting the required parameters of the ckbox attribute. Please note that in the ckbox.tokenUrl configuration option we pass the URL of the token endpoint created in one of the previous steps, which will be used to obtain JWT tokens used to authenticate users in CKBox.

# CKBox as file picker

One of the common scenarios is to use CKBox as a file picker, where the user can choose one of the files stored in the file manager. After choosing the file, we want to obtain information about the chosen files, especially their URLs. This can be achieved using the assets.onChoose callback passed as the CKBox’s configuration option:

Pages/filePicker.cshtml

@page

@{
    Layout = "Shared/_Layout";
    var tokenUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/token";
}

<div>
    <input type="text" id="file-url"></input><button id="choose">Choose file</button>
        <div id="ckbox"></div>
        <script>
            document.getElementById('choose').addEventListener('click', function () {
                CKBox.mount(document.querySelector('#ckbox'), {
                    tokenUrl: '@tokenUrl',
                    dialog: true,
                    assets: {
                        // Callback executed after choosing assets
                        onChoose: (assets) => {
                            document.getElementById('file-url').value = assets[0].data.url;
                            assets.forEach((asset) => {
                                console.log(asset.data.url);
                            })
                        }
                    }
                });
            });
        </script>
</div>

The example will be available under http://localhost:5063/filePicker.

# CKBox in full-page mode

To start CKBox in full-page mode, you can attach it to the document.body and adjust the required CSS styles:

Pages/fullPage.cshtml

@page

@{
    Layout = "Shared/_Layout";
    var tokenUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}/token";
}

@section Head {
    <style>
            html, body {
                margin: 0;
                padding: 0;
                height: 100vh;
            }
    </style>
}

<div>
    <script>
        CKBox.mount(document.body, {
            tokenUrl: '@tokenUrl',
        });
    </script>
</div>

The example will be available under http://localhost:5063/fullPage.

# Complete code

On GitHub, you can find the complete code of the application described in this guide.