Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

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!