Guides and Examples

Checking webhook signatures (HMACs)

when kindly sends data to external services (e g when triggering a webhook to a service owned by you), the payload will be authenticated with a hash based message authentication code (hmac) https //en wikipedia org/wiki/hmac the key used to create the hmac is a shared secret, and you verify it by running the algorithm yourself with the payload and the key to re create the hmac this verifies that the sender of the message knows the secret key secret key the secret key used in the algorithm depends on which kind of request is being sent, and you can find your keys in the kindly platform, under your workspace settings > general > security if you suspect that the key might have been compromised, you can revoke it and issue a new key in the web interface algorithm the hmac is created with the hmac sha256 algorithm, then encoded in base 64 the hmac is attached to the request in the kindly hmac header you should also read the value of the kindly hmac algorithm request header and verify that it matches hmac sha 256 (base64 encoded) if it becomes necessary to change the algorithm in the future, this value will change to let you know request headers\["kindly hmac"] = base64 encode(hmac sha256(request body, secret key)) request headers\["kindly hmac algorithm"] = "hmac sha 256 (base64 encoded)" verifying authenticity verifying a hmac sha256 value should be quite simple by using a crypto library in your preferred language js/node example const cryptojs = require('crypto js'); function verifyhmac( receivedhmac, // received in kindly hmac request header datastring, // received in request body (stringified json) key // shared secret, known by your service ) { const generatedhmac = cryptojs hmacsha256(datastring, key); const generatedhmacbase64 = cryptojs enc base64 stringify(generatedhmac); return generatedhmacbase64 === receivedhmac; } // test the function const receivedhmac = "ueed0q7ew9btdx6lfvvlpwkzqbwdbknsqkg1c27cx7q="; const datastring = '{"foo" 1,"bar" 2}'; const key = "examplekey"; if (!verifyhmac(receivedhmac, datastring, key)) { throw new error("hmac did not authenticate"); } python example the hashing algorithm works on bytes , so we make sure to do any necessary conversions from unicode str to bytes import base64 import hashlib import hmac def verify kindly hmac(headers dict, request data bytes, secret key str) \# note http headers are case insensitive alg = headers get('kindly hmac algorithm') mac = headers get('kindly hmac') if not (alg and mac) print("hmac was not provided") return false if alg != "hmac sha 256 (base64 encoded)" print("unexpected hmac algorithm") return false msg = request data key = secret key encode('utf 8') received hmac b64 = mac encode('utf 8') generated hmac = hmac new(key=key, msg=msg, digestmod=hashlib sha256) digest() generated hmac b64 = base64 b64encode(generated hmac) match = hmac compare digest(received hmac b64, generated hmac b64) if not match print('hmacs did not match {} {}' format(received hmac b64, generated hmac b64)) return false return true c# / net 7 example depending on your setup you may need to rewind the request body stream after consuming it (see code comment) public async task\<bool> verifyhash(httprequest request, string signingkey) { if (!request headers trygetvalue("kindly hmac algorithm", out var hmacalgorithm) || hmacalgorithm != "hmac sha 256 (base64 encoded)") { throw new httprequestexception("unexpected hmac algorithm", null, system net httpstatuscode badrequest); } if (!request headers trygetvalue("kindly hmac", out var receievedhmac) || string isnullorempty(receievedhmac)) { throw new httprequestexception("hmac was not provided", null, system net httpstatuscode badrequest); } using var hasher = new hmacsha256(encoding utf8 getbytes(signingkey)); var hash = await hasher computehashasync(request body); // uncomment the line below to rewind the stream if request buffering is not enabled // request body seek(0, seekorigin begin); var hash b64 = convert tobase64string(hash); return receievedhmac == hash b64; }