Webhook Request Signature
Each webhook request that Quadrata emits will contain a header signature that an integrating application can use to verify the authenticity of the message.
Request signatures are sent as Base64 Encoded SHA384 strings, in the X-WEBHOOK-SIGNATURE request header.
Verifying The Request Signature
To verify the request signature, use Quadrata's public signing key for the respective environment.
Quadrata Public Signing Keys
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE1iwh7gCfjdQRo/r82k8ErKiLO+cbPJkY
zqAqrPe0le6vjYY9aTp92ps37mcHzLjitslHeG4f5nSuBXKz8WXuwSyWhUW6EyZb
v/1tUfucvjBRrT7Yks6u6jmpwPmIuaqI
-----END PUBLIC KEY----------BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEOuY3rbyrujXxVEWq2X70uRa53ySTjwKR
j1ueDjYuzMegLrxIRiCXWMPtrVuqE0FcZ2YmJSiTaoDsq4yYMJw7fxi6nUj/8bzT
4+IxIok9qaEq9IbX6Bo/95vAu5bwO3rf
-----END PUBLIC KEY-----Code Samples
// NOTE: this is using Quadrata's Staging webhook signing key
const publicKey = `-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE1iwh7gCfjdQRo/r82k8ErKiLO+cbPJkY
zqAqrPe0le6vjYY9aTp92ps37mcHzLjitslHeG4f5nSuBXKz8WXuwSyWhUW6EyZb
v/1tUfucvjBRrT7Yks6u6jmpwPmIuaqI
-----END PUBLIC KEY-----`;
/**
* Verify a signed message
* @param {string} message The request body stringified
* @param {string} signature The base64 encoded signature
* @returns {boolean}
*/
function verifySignature(message, signature) {
const signature = Buffer.from(signature, 'base64');
// create a verifier and verify the signature
const verifier = crypto.createVerify('sha384');
verifier.update(message);
return verifier.verify(publicKey, signature);
}const crypto = require('crypto');
const express = require('express');
// NOTE: this is using Quadrata's Staging webhook signing key
const publicKey = `-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE1iwh7gCfjdQRo/r82k8ErKiLO+cbPJkY
zqAqrPe0le6vjYY9aTp92ps37mcHzLjitslHeG4f5nSuBXKz8WXuwSyWhUW6EyZb
v/1tUfucvjBRrT7Yks6u6jmpwPmIuaqI
-----END PUBLIC KEY-----`;
/**
* Verify a signed message
* @param {string} message The request body stringified
* @param {string} signature The base64 encoded signature
* @returns {boolean}
*/
function verifySignature(message, signature) {
const signature = Buffer.from(signature, 'base64');
// create a verifier and verify the signature
const verifier = crypto.createVerify('sha384');
verifier.update(message);
return verifier.verify(publicKey, signature);
}
// initialize the express application
const app = express();
// parse JSON request body into req.body
app.use(express.json());
// webhook request handler
app.post('/quadrata-webhook', (req, res) => {
// verify the request has a webhook signature
if (!('x-webhook-signature' in req.headers)
|| !req.headers['x-webhook-signature']
) {
return res.status(400).send();
}
// stringify the request body
const signatureMessage = JSON.stringify(req.body);
// extract the base64 signature from headers
const signature = request.headers['x-webhook-signature'];
// verify the signature
if (!verifySignature(signatureMessage, signature)) {
return res.status(400).send();
}
// webhook is valid, flush a successful response
res.status(200).send();
// process the event on your servers (you implement this)
processQuadrataWebhook(req.body)
.catch(console.error);
});
// listen for traffic
const port = process.ENV.port;
const hostname = process.ENV.hostname;
app.listen(port, hostname);import base64
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, utils
# NOTE: using Quadrata's Staging webhook signing key
PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE1iwh7gCfjdQRo/r82k8ErKiLO+cbPJkY
zqAqrPe0le6vjYY9aTp92ps37mcHzLjitslHeG4f5nSuBXKz8WXuwSyWhUW6EyZb
v/1tUfucvjBRrT7Yks6u6jmpwPmIuaqI
-----END PUBLIC KEY-----"""
def verify_signature(message: str, b64_signature: str) -> bool:
public_key = serialization.load_pem_public_key(
PUBLIC_KEY_PEM.encode('utf-8')
)
try:
public_key.verify(
base64.b64decode(b64_signature),
message.encode('utf-8'),
ec.ECDSA(hashes.SHA384()),
)
return True
except:
return FalseIf using json.dumps to marshal the JSON body payload into a string, be sure to override the separators:
json.dumps(request_body, separators=(',', ':'))
Last updated