javascriptvalidationexpresswebhookskentico-kontent

Validating Kentico Cloud webhooks signatures in Express.js


How to validate webbooks signature using express.js?

In docs, there is a section about notification signatures but I don't know how to combine it with Express.js

This question is a migrated from official Kentico Cloud Forum, that would be deleted.


Solution

  • In the API reference, there is a sample describing webhook validation in various languages including node.js.

    If you want to use express.js you could start with this template code:

    const express = require('express');
    const crypto = require('crypto');
    
    // Create a new instance of express
    const app = express();
    
    // Set up a raw bodyparser to read the webhook post
    const bodyParserRaw = require('body-parser').raw({
        type: '*/*',
    });
    
    function webhookValidator (req, res, next) {
        // get the header signature from the webhook request
        const givenSignature = req.headers['x-kc-signature'];
    
        // throw error if it's missing
        if (!givenSignature) {
            console.log('Missing signature');
            return res.status(409).json({
                error: 'Missing signature'
            });
        }
    
        // create HMAC from the raw request body
        let hmac = crypto.createHmac('sha256', [your-webhook-secret-key]);
        hmac.write(req.body);
        hmac.end();
    
        // get a base64 hash from HMAC
        let hash = hmac.read().toString('base64');
    
        // check validity with timingSafeEqual
        let webhookValid = false;
        try {
            webhookValid = crypto.timingSafeEqual(Buffer.from(givenSignature, 'base64'), Buffer.from(hash, 'base64'));
        } catch (e) {
            webhookValid = false
        }
    
        // return validity
        if (webhookValid) {
            return next();
        } else {
            console.log('Invalid signature');
            return res.status(409).json({
                error: 'Invalid signature'
            });
        }
    }
    
    // create a route and pass through the bodyparser and validator
    app.post('/webhook', bodyParserRaw, webhookValidator, ( req, res, next ) => {
        // If execution gets here, the HMAC is valid
        console.log('webhook is valid');
    });
    

    EDIT

    You can use the Kontent webhook helper library to quickly verify the webhook notifications and their signatures. The library is available as @kentico/kontent-webhook-helper npm package and helps you avoid common problems when calculating the hash.