Duo Authentication Header

danielmenter
Tera Expert

Hi all like numerous others I am trying to setup some automation directly from ServiceNow to our duo client. However I am unable to get authorized.

https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB1641630

https://duo.com/docs/adminapi#authentication


I have created the following code to test the example from the duo api documentation.
I am getting a similar result to the example but neither GlideDigest or the function from the kb article produce a matching result.

var duotenant = "xxxxxxxx";
var resourceMethod = "POST";
var resourcePath = "admin/v1/users";
var parameters = "realname=First%20Last&username=root";
var date = "Tue, 21 Aug 2012 17:29:18 -0000";
var example = "RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg==";
var authHeader = generateAuthHeader(date, resourceMethod, duotenant, resourcePath, parameters);
gs.info("\nExample "+ example + "\nGenerated " + authHeader);

function generateAuthHeader(date, resourceMethod, duotenant, resourcePath, parameters) {
    var secretKey = "Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep";
    var integrationKey = "DIWJ8X6AEYOR5OMC6TQ1";

    //create canonical representation
    var canonicalAuthHeader = date + "\n";
    canonicalAuthHeader += resourceMethod.toUpperCase() + "\n";
    canonicalAuthHeader += "api-" + duotenant.toLowerCase() + ".duosecurity.com" + "\n";
    canonicalAuthHeader += resourcePath + "\n";
    canonicalAuthHeader += parameters;

    //compute the HMAC-SHA1 of this canonical representation
    var mac = new GlideCertificateEncryption;
    var key = GlideStringUtil.base64Encode(secretKey);
    var hmackey = mac.generateMac(key, "HmacSHA1", canonicalAuthHeader);

    

    //Using GlideDigest with getSHA1Hex() method to get hexadecimal ASCII of the hash value calculated using "GlideCertificateEncryption"
    var digest = new GlideDigest();
    //var password = digest.getSHA1Hex(hamckey).toLowerCase();
 var password = toHexString(hmackey);

    var signature = GlideStringUtil.base64Encode(integrationKey + ":" + password);

    function toHexString(credentials) {
        // Decode base64 to binary
        var binaryData = GlideStringUtil.base64DecodeAsBytes(credentials);
        // Convert binary to hexadecimal
        var hex = '';
        for (var i = 0; i < binaryData.length; i++) {
            var byteValue = binaryData[i];
            var hexString = ('0' + (byteValue & 0xFF).toString(16)).slice(-2); // Convert byte to hexadecimal
            hex += hexString;
        }
        return hex;
    }

    return signature;
}


Using the ToHexString from the KB
Example
RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg==
Generated
RElXSjhYNkFFWU9SNU9NQzZUUTE6ODcyMmYyMjRhNDBhNWJlZGM3YzNmODQxYTE3MzMwOTVjZTU4Mzg4YQ==

Using the GlideDigest
Example
RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg==
Generated
RElXSjhYNkFFWU9SNU9NQzZUUTE6ODgwYjU2MjJhODBiMmNlZTllYWU3N2UwMWI4YzEzN2Q0ZWIyYmUyNw==


1 ACCEPTED SOLUTION

danielmenter
Tera Expert

Was able to get it working.

var duotenant = "xxxxxxxx";
var resourceMethod = "POST";
var resourcePath = "/admin/v1/users";
var parameters = "realname=First%20Last&username=root";
var date = "Tue, 21 Aug 2012 17:29:18 -0000";
var example = "RElXSjhYNkFFWU9SNU9NQzZUUTE6YzFlZjQzNzY3YzNlYjNiMzI1OGRiZGRjYTZmOGQwOTQxZTA4NWI5Mg==";
var authSignature = generateAuthSignature(date, resourceMethod, duotenant, resourcePath, parameters);
gs.info("\nExample "+ example + "\nNew Sig " + authSignature);

function generateAuthSignature(date, resourceMethod, duotenant, resourcePath, parameters) {
    var secretKey = "Zh5eGmUq9zpfQnyUIu5OL9iWoMMv5ZNmk3zLJ4Ep";
    var integrationKey = "DIWJ8X6AEYOR5OMC6TQ1";

    //create canonical representation
    var canonicalAuthHeader = date + "\n";
    canonicalAuthHeader += resourceMethod.toUpperCase() + "\n";
    canonicalAuthHeader += "api-" + duotenant.toLowerCase() + ".duosecurity.com" + "\n";
    canonicalAuthHeader += resourcePath + "\n";
    canonicalAuthHeader += parameters;

    //compute the HMAC-SHA1 of this canonical representation
    var mac = new GlideCertificateEncryption;
    var key = GlideStringUtil.base64Encode(secretKey);
    var hmackey = mac.generateMac(key, "HmacSHA1", canonicalAuthHeader);

    //Send this signature as hexadecimal ASCII (i.e. not raw binary data).
    var password = toHexString(hmackey);

    //integration key as the username and the HMAC-SHA1 signature as the password
    var signature = GlideStringUtil.base64Encode(integrationKey + ":" + password);
    return signature;

    function toHexString(credentials) {
        // Decode base64 to binary
        var binaryData = GlideStringUtil.base64DecodeAsBytes(credentials);
        // Convert binary to hexadecimal
        var hex = '';
        for (var i = 0; i < binaryData.length; i++) {
            var byteValue = binaryData[i];
            var hexString = ('0' + (byteValue & 0xFF).toString(16)).slice(-2); // Convert byte to hexadecimal
            hex += hexString;
        }
        return hex;
    }
}

View solution in original post

9 REPLIES 9

Why do I keep getting {"code": 40101, "message": "Missing request credentials", "stat": "FAIL"}

I don't know what I a missing 😞 

 

Ping

 

I was able to use this postman collection for testing the connection to duo outside of ServiceNow.

https://www.postman.com/dn1997/dan-nash-s-public-workspace/documentation/uy4k94e/duo-api-for-postman

Hi Daniel,

 

How did you implement this in ServiceNow? Did you use Flow Designer?

I put the script above to generate the auth header in a script include and updated it to pull in relevant duo client info.

Next, I created a rest message to duo in the rest message table.

danielmenter_0-1755287310526.png


Next I created methods created to consume these duo endpoints.

danielmenter_1-1755287402273.png

https://duo.com/docs/adminapi#retrieve-users
https://duo.com/docs/adminapi#modify-user

I then created custom actions for use in flow designer that consume both the script include and the rest methods.

danielmenter_8-1755289537342.png

danielmenter_3-1755288152091.png

Use in flow designer.

danielmenter_5-1755288878972.png

 

Thank you soooo much for this Daniel!!!

I will try this and will let you know! This is a big help. 

Truly appreciate your help!