Code Docs


Pluvo Developers Docs


Back to website:

Dutch: Pluvo.nl

English: Pluvo.eu

Introduction

To check if the POST request received at your endpoint is a valid Pluvo request. The request contains two headers X-Signature and X-Signature-Salt,

You can check the signature by calculating a SHA1-HMAC signature for the request body, the key should be a SHA1 combination of the salt received in the headers and your webhook key. Then you can compare the calculated signature with the signature in the headers and see if the request is valid.

Node.js Example

This is a node example using express with a custom middleware to verify that the request is valid.

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');

const sigHeaderName = 'X-Signature';
const saltHeaderName = 'X-Signature-Salt';

const webhookSecret = '[your-webhook-secret]';

const app = express();

// Parse the body as text because we don't want to parse the body data
app.use(bodyParser.text({
    type: 'application/json'
}));

function verifyPostData(req, _res, next) {
    const payload = req.body;

    if (!payload) {
        return next('Request body empty');
    }

    // Get salt and signature from request headers
    const salt = req.get(saltHeaderName) || '';
    const sig = req.get(sigHeaderName) || '';

    // Create sha1 hashed key using the salt en the secret
    const key = crypto
        .createHash('sha1')
        .update(`${salt}${webhookSecret}`)
        .digest();

    // Calculate SHA1-HMAC signature for using the payload and key
    const hmac = crypto
        .createHmac('sha1', key)
        .update(payload)
        .digest('base64');

    // Replace the "+" and "\\" and remove trailing "="
    const digest = hmac
        .replace(/\\+/g, '-')
        .replace(/\\//g, '_')
        .replace(/=+$/, '');

    // Check if digest and signature match using `crypto.timingSafeEqual`
    if (
        sig.length !== digest.length ||
        !crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(sig))
    ) {
        return next(
            `Request body digest (${digest}) did not match ${sigHeaderName} (${checksum})`
        );
    }

    return next();
}

app.post('/webhook/', verifyPostData, function (req, res) {
    res.status(200).send('Request body was signed');
});

app.listen(3000, () => {
    console.log(`Example app listening at http://localhost:3000`);
});

PHP Example

function calculate_signature($salt, $key, $payload){
    return rtrim(strtr(base64_encode(hash_hmac("sha1", $payload, sha1($salt . $key, true), true)), '+/', '-_'), '=');
}