Skip to main content

Security & Authentication

Each webhook event sent by RevOps contains a secure hash that clients can use to verify that a request originated from our servers.

Moreover, to improve security and support separation of concerns, each webhook configured within RevOps contains a dedicated Verification Key. Clients should always verify events before processing to ensure data integrity and security.

HTTP Security​

RevOps requires that all webhooks terminate with an https:// URL. This keeps event information secure as it is transmitted between our servers to yours.

Webhook HTTP Headers​

RevOps generates an HMAC signature of the request's body contents and includes this information as a custom header:

NameDescription
X-RevOps-Content-HmacA hex encoded SHA256 HMAC signature of the request's payload generated using the webhook's verification key.

Event Hash Verification (Python)​

The following example shows how to use a webhook Verification Key, along with the content HMAC signature, to verify incoming events:

Note: Your webhook verification key can be found at: /integrations/webhooks/setup

import binascii
import hashlib
import hmac

from flask import Flask, jsonify, request

REVOPS_CONTENT_HMAC_HEADER = "X-Revops-Content-Hmac"
REVOPS_VERIFICATION_KEY = b""

app = Flask(__name__)


@app.route("/", methods=["POST"])
def event_verification():
""" Simple handler that verifies RevOps webhook events """
revops_hash = request.headers.get(REVOPS_CONTENT_HMAC_HEADER)
if revops_hash == None:
return (
jsonify({"Status": "Missing RevOps HTTP Header: X-Revops-Content-Hmac"}),
401,
)

content_hash = binascii.hexlify(
hmac.new(
REVOPS_VERIFICATION_KEY, request.data, digestmod=hashlib.sha256
).digest()
).decode()
if revops_hash != content_hash:
e = Exception()
return (
jsonify(
{
"Status": f"Invalid Content HMAC: {content_hash} (Expected: {revops_hash})"
}
),
401,
)

return jsonify({"Status": "Message verified!"}), 200


if __name__ == "__main__":
app.run(debug=True, port=8081)

Event Hash Verification (Node.js)​

The following Node.js code shows how to use Express and the body-parser library to verify the HMAC signature.

import { createHmac } from "crypto";
import express from "express";
import bodyParser from "body-parser";

// Use an environment variable for the secret verification key
const REVOPS_WEBHOOK_VERIFICATION_KEY = process.env.REVOPS_WEBHOOK_VERIFICATION_KEY;
// n.b. headers on the express request object are all lowercase
const REVOPS_HMAC_HEADER = 'X-Revops-Content-Hmac'.toLowerCase();

const app = express();
const port = 8080;

class HmacVerifyError extends Error { }

/**
* @param {Buffer} buf - The raw bytes of the request body.
*/
const verifyRevopsWebhook = (req, _res, buf, _encoding) => {
// Note: this is where the following verification could be conditionally applied.
// For example (pseudocode):
// if (req.path !== "/revops/incoming_webhooks") { return; }
//
const hmac = createHmac('sha256', REVOPS_WEBHOOK_VERIFICATION_KEY);
hmac.update(buf.toString());
const actualDigest = hmac.digest('hex');
const expectedDigest = req.headers[REVOPS_HMAC_HEADER];

if (actualDigest !== expectedDigest) {
throw new HmacVerifyError(`Revops webhook verification failed. Got: ${actualDigest}, expected: ${expectedDigest}`);
}
};

// This middleware will run for every request
app.use(bodyParser.json({ verify: verifyRevopsWebhook }))

app.post('/', (req, res) => {
console.log(req);
res.send('Success');
});

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