Duo API Integration: 401 Error Due to Invalid Signature - Correct Method for Generating HMAC-SHA1

subbarayudu043
Tera Contributor

Hi all,
I am encountering an issue with authenticating the Duo server when making a REST call to one of the Duo API endpoints from ServiceNow. I have created a UI action (a button DUO PUSH on the incident form) and written a ServiceNow JavaScript for the REST call. The integration uses Basic Authentication, and the Duo API documentation specifies that the client secret key should be encrypted using HMAC-SHA1.

Here is the relevant part of my code:

 

 function sendPush() {

     try {

 

         //getting the caller id

         var user_id = current.caller_id.user_name;

 

         // Using the below code to get the date as mentioned in the cisco duo account api documentation.

         //Example Date formate :Tue, 09 Aug 2024 17:29:18 -0000

         var gdt = new GlideDate();

         var duo_date = gdt.getByFormat("EEE, dd MMM YYYY HH:mm:ss ZZ");

         var method = "GET"; // HTTP Method type.

         var host = "api-axxxxxx.duosecurity.com"; //Host end point

         var path = "/auth/v2/check"; //to verify that the Auth API integration and secret keys are valid, and that the signature is being generated properly.

         var param = ""; // passing empty parameters. As per the Duo documentation if are not passing any parameters, and empty value should be provided. 

 

         //concatenate these components with (line feed) newlines.

         var data = duo_date + '\n' + method + '\n' + host + '\n' + path;

         gs.log("BS: Data " + data);

 

         //Using "GlideCertificateEncryption" to generate HMAC-SHA1.

         var mac = new GlideCertificateEncryption();

         var skey = "Secret key shared by the client"; //Duo secret key

         key = GlideStringUtil.base64Encode(skey);

         var hash = mac.generateMac(key, "HmacSHA1", data);

         gs.log("BS: has " + hash);

 

         //Using GlideDigest with getSHA1Hex() method to get hexadecimal ASCII of the hash value calculated using "GlideCertificateEncryption"

         var digest = new GlideDigest();

         var password = digest.getSHA1Hex(hash).toLowerCase();

         gs.log("BS: Hmach pass " + password);

 

         //Converting ikey & Hmac password to base64 encoded

         var ikey = "Intgertaion key shared by the client"; //Duo integration key

         var basic_auth = ikey + ":" + password;

         var base64_basic_auth = GlideStringUtil.base64Encode(basic_auth);

         gs.log("BS: Base64 encoded ikey & hmac password " + base64_basic_auth);

 

         //Endpoint of the REST API call.

         var request = new sn_ws.RESTMessageV2();

         var endpoint = "https://api-a5025657.duosecurity.com/auth/v2/check";

         gs.log("BS: endpoint " + endpoint);

         request.setEndpoint(endpoint);

 

         //we need to pass the Authorization Header manually

         var authorization = "Basic " + base64_basic_auth;

 

         //Headers for the REST API call to integrate with the cisco duo.

         request.setRequestHeader('Authorization', authorization);

         request.setRequestHeader("Date", duo_date);

         request.setHttpMethod('GET');

         request.setRequestHeader('Content-Length', '0');

         request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

 

         var response = request.execute();

         var responseBody = response.getBody();

         gs.log("BS: response body " + responseBody);

         var httpStatus = response.getStatusCode();

         gs.log("BS: httpstatus " + httpStatus);

         var duoData = JSON.parse(responseBody);

 

         if (duoData.result == 'allow') {

             gs.addInfoMessage('The DUO push was accepted by the user.');

             action.setRedirectURL(current);

         }

 

         if (duoData.result == 'deny') {

 

             gs.addErrorMessage('The DUO push was denied by the user.');

             action.setRedirectURL(current);

 

         }

 

         if (duoData.result == 'user-not-valid') {

 

             gs.addErrorMessage('The user is not active in DUO or has no enrolled devices...');

             action.setRedirectURL(current);

 

         }

 

         gs.info('DUO: ' + responseBody);

     } catch (ex) {

         var message = ex.message;

     }

 }

 sendPush();

 



However, despite following the Duo API documentation, I am receiving a 401 error with the message:

{
"code": 40103,
"message": "Invalid signature in request credentials",
"stat": "FAIL"
}

Could you please assist in troubleshooting this issue? Specifically, I need guidance on the following:

Verification of HMAC-SHA1 Implementation: Is the method I’m using to generate the HMAC-SHA1 hash correct? Should the Secret key be encoded or processed in a different manner?


Correct Usage of GlideDigest: Are there any specific configurations or adjustments needed for the GlideDigest class to ensure proper HMAC-SHA1 hash generation?

 

For your reference, you can find the Duo API documentation on how to encrypt the client secret key using HMAC-SHA1 at the following link: https://duo.com/docs/authapi#api-details . To locate the relevant encryption steps, please navigate to API Details > Authentication.

 

Your assistance in resolving this issue would be greatly appreciated.

 

Thank you.

2 REPLIES 2

Scott_Robinson
Tera Contributor

Did you ever figure this one out? I am facing the same issue.

danielmenter
Tera Expert