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:

Name Description
X-RevOps-Content-Hmac A 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}`);
});

Reference Implementations

The above example contains the minimum amount of code required to get a quick proof-of-concept up and running. More complete reference implementations are available online for Python, Go, and Ruby:

revops-webhook-server

revops-webhook-server-go

revops-webhook-server-ruby