DEV Community

Cover image for plugin for headless CMS (part 2)

Posted on • Originally published at plugin for headless CMS (part 2)


As elaborated in the article "Cross publishing to" (on || on, I created a plugin for that cross posts to by utilizing the webhook functionality of I did this as a proof of concept of how to build custom plugins for in general.

While the general approach was proven, I had outstanding problems to tackle, while allowing for the folowing rules:

- modifications to's models & functionality are out of the question to stay future proof

- saving credentials as plain-text in the MySQL database is not an option

- somehow prohibit public access while not having a JWT from the user present in the webhooks

Outstanding problems

So, what where the remaining issues? Well, first of all, having the API key as server-side credentials only enabled one user-account. That is a big problem considering that a multi-user system should enable anyone with a user account to post as individual author using an individual user account. Letting people publish in my name was certainly not an option. has the ability to set tokens that are transmitted with the webhooks as bearer tokens. However, these tokens are stored in plain text and are intended to provide identification of the origin of a call, not to securely store credentials. So what to do?

The principle

So here is the solution I came up with: I would create an endpoint as a route for setup purposes. There you can input your API-key and send it to the plugin where a instance-specific encryption-key ( outside the public web-root via neoan3 credentials ) would encrypt and serialize it.

In a second step, the result of this operation is then provided as token to the webhook pointing at the plugin and stored in the database.

Whenever the plugin receives a call it can now revert this operation ( deserialize & decrypt ) and use the result (the plain API-key) for communication with, at which point the API will either recognize the user and authentication or not.

Since the POST-endpoint is used by the webhook and I wanted to contain the plugin to one component, I used this GET function:

function getDevTo(array $body = [])
        // make sure only logged in users can execute this
        $answer = [];
        if (isset($body['apiKey'])) {
            // assign
            $this->apiKey = $body['apiKey'];
            // use existing header construction
            $header = $this->curlHeader();
            // make a test-call with given API key to provide feedback for the frontend
            $testAnswer = Curl::curling('',[],$header, 'GET');
            // obtain neoan3 credentials
            $credentials = getCredentials();
            $key = $credentials['blua_devto']['salt'];
            // encrypt & serialize
            $encrypted = Ops::serialize(Ops::encrypt($body['apiKey'], $key));
            $answer = ['token' => $encrypted, 'test' => $testAnswer];

        return $answer;

Enter fullscreen mode Exit fullscreen mode

In order to retrieve the API key, I had to make changes to the POST function as well:

function postDevTo(array $body)
        try {
            $credentials = getCredentials();
            // check token
            if (!isset($_SERVER['HTTP_AUTHORIZATION'])) {
                return ['webhook' => 'denied'];

            $this->apiKey = $this->getApiKey($credentials, substr($_SERVER['HTTP_AUTHORIZATION'], 7));

            switch ($body['event']) {
                case 'created':
                case 'updated':
                    // find existing
                    $update = $this->investigateStoreObject($body['payload']['store']);
                    $devBody = $this->transformPayload($body['payload']);
                    $this->sendToDevTo($devBody, $update);
                case 'deleted':
        } catch (\Exception $e) {
            throw new RouteException('Unable to execute plugin', 500);
        return ['webhook' => 'received'];
Enter fullscreen mode Exit fullscreen mode

I uploaded the changes to and here I am using it!


Want to try it out?

Sign up at and then visit for the setup.

Top comments (1)