Skip to content

How to

How to validate Jira's webhooks using "secrets"

Jira does not support authentication on webhooks but it now supports "secrets". It works by using a shared token called a secret. Jira can generate this token and will then include a header (x-hub-signature) on the messages send from that webhook. The value of that header contains a calculated HMAC signature, using the message body, that the receiver can validate by calculating the HMAC signature using the request message and the shared secret. If the calculated value is the same as the value in the header we know the message has not been tampered with.

To do so we need a new Scripted REST API with no authentication. In the Scripted REST API we will capture the header, do the calculations and check if the token is valid. If so, we will then send the message to the SofieHub endpoint with authentication. This Scripted REST API needs to be in a scoped application because of the ServiceNow API's that we are using in this code.

(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
    var secret                       = "JIRASECRET";
    var sofie_webservice             = "jira";
    var sofie_webservice_http_method = "POST";
    var username                     = "api.user";
    var password                     = "password";

    // inline function to convert a hex string to base64
    function hexToBase64(str) {
        var hexPairs = str.match(/[\da-fA-F]{2}/g) || [];
        var byteArray = [];
        for (var i = 0; i < hexPairs.length; i++) {
            byteArray.push(parseInt(hexPairs[i], 16));
        }
        return GlideStringUtil.base64Encode(String.fromCharCode.apply(null, byteArray));
    }

    // build the SofieHub endpoint URL
    var env = gs.getProperty('instance_name', 'invalid-instance-name');
    var endpoint = 'https://' + env + '.service-now.com/api/x_sofbv_sofiehub/sofiehub/' + sofie_webservice;

    // get the request message
    var body = request.body.dataString;
    // get the header, split it by = and return the second value. This contains the actual calculated value in hex.
    var headerValue = request.getHeader('x-hub-signature').split('=')[1];
    //  convert the headerValue from hex to base64
    var header = hexToBase64(headerValue);
    // calculate the signature with the provided secret
    var check = new CertificateEncryption().generateMac(gs.base64Encode(secret), 'HmacSHA256', body);

    // check if our value is the same as the value in the header. If not; return.
    if (header != check) {
        gs.info("Secret validation failed");
        return;
    }

    // validation was succesful, send the message to the SofieHub endpoint
    var sm = new sn_ws.RESTMessageV2();
    sm.setHttpMethod(sofie_webservice_http_method);
    sm.setEndpoint(endpoint);
    sm.setBasicAuth(username, password);
    sm.setRequestBody(body);
    sm.execute();

})(request, response);