Preauth API failing but check and ping are good

Hi Team,

I reffed to https://duo.com/docs/authapi#endpoints to use DUO Auth API for check and preauth.
i used this https://github.com/duosecurity/duo_client_java/blob/master/duo-client/src/main/java/com/duosecurity/client/Http.java and with this the ping and check API’ are success but Preauth api i am getting 40103 Invalid signature in request credentials and i am using the correct ikey and skey that are created for Auth API in duo admin console

Could you please suggest me on how to resolve this issue.

Thank you

Per this Duo KB article, please try the following suggestions:

Verify that the signature is encoded in hexadecimal ASCII; is using the correct HMAC-SHA1 signature as the password; lists parameters in alphabetical order.

@DuoKristina what exactly does that mean? I’m using Postman simply to test and I’m receiving the same error.

Hey @humblecoder,

Did you take a look at the KB article I referenced, which lists common Duo error codes and possible causes?

@DuoKristina I did indeed. In fact I’d seen it before coming here. I don’t mean to be obtuse, but are you referring to the parameter structure as a “signature”? I’ve never seen “signature” in that context and have no idea how it’s being used.

Also, while the KB article ostensibly says what the fix is, it doesn’t make clear how that is accomplished. I mean, from my perspective, I’m sending the request in plain text via PostMan. I can’t imagine what else to fix.

Regards

By “signature” we do refer to the computed SHA1 of the API request (with parameters) as described in here: Auth API | Duo Security.

Are you also having a success on /check but fail on /preauth? Or, is nothing successful?

@DuoKristina Actually, I can’t get anything to succeed. Here is the JS I’m using to create the relevant params in PostMan:

	let curDate = (new Date()).toUTCString()
	let myHost = 'HIDDEN-HOST'

	let params = {
			'device': 'auto',
			'factor': 'push',
			'username': 'HIDDEN-NAME'
	}

	let urlEncodedParams = []

	_.forOwn(params,(v,k)=>{
		urlEncodedParams.push(encodeURIComponent(k) + '=' + encodeURIComponent(v))
	})

	let sigComponents = {
			'date': curDate,
			'host': myHost,
			'method': 'POST',
			'path': '/auth/v2/auth',
			'urlParam': urlEncodedParams.join('&')
	}

	let ■■■■ing = Object.values(sigComponents).join('\r\n')

	let hmacSignature = CryptoJS.HmacSHA1(sigComponentValueString, 'HIDDEN-STRING').toString()

	pm.environment.set("env_new_date", curDate)
	pm.environment.set("env_hmac_signature", hmacSignature);

	console.log(sigComponentValueString)

Obviously a convoluted process (especially given the nature of the requirement), but is there anything glaringly wrong.

@DuoKristina It would be nice to have far more descriptive errors, especially in “development”. Perhaps we could see on our dashboard what was sent vs what the server compared it to.

I am also having the same issue on POSTMAN. I put my skey(Password) and ikey(Username) under Authorization, after selecting Basic Auth.
POSTMAN created the Authorization Header. I am passing “Date” and “Content-Type” in the header".
I am getting {“code”: 40103, “message”: “Invalid signature in request credentials”, “stat”: “FAIL”}

@DuoKristina - I did try to use the python code at Auth API | Duo Security to generate “Authorization header” but code is throwing
NameError: name ‘unicode’ is not defined

I was running into the same problem. I figured out the solution to it with postman. When DUO api is referring to sending the signature you need to dynamically build the signature using the pre-request script feature in postman. In the API documentation they talk about constructing the signature, this is what they are referring to. In postman the pre-request script will set the signature as a postman environment variable which you will access in the header section. Below is the script I used in the pre-request script to build using their example in the api section and it gave me the correct signature.
You can verify the information by opening up the postman console. In your postman headers you will need to add Date with value of {{timestampHeader}} and Authorization with value of {{hmacAuthHeader}}
The Body tab you will want to select x-www-form-urlencoded. My recommendation would be to start out small and use api: /auth/v2/preauth for your own example. Use the username and alias for value. Make sure that the pre-request script body value matches the same alias value you use in the body section. The script below will fail using their information from the api page. When you replace everything with your own information it should work. This is how I got mine to work. The added console lines will show up in postman console where you can verify the different steps of information. Please be aware the hostname below will need to be replaced because the forum removed the information for client key and secret key. Go to the Auth API | Duo Security page and search for construct the signature and use the host listed, the ikey(CLIENT_KEY) and the secret key they have listed in their test test. One last thing I was never able to use the full username email instead I added a new alias with a short name to the user and made sure that what I was requesting on for username. If I used the full email it was never able to work. example: bob@bob.com I added alias bob and did username=bob . Hopefully this will help others understand what is needed. Good luck

function getAuthHeader(httpMethod, requestUrl, requestBody) { console.log(requestBody); //body data console.log(httpMethod); // http type: POST, GET, ETC
var CLIENT_KEY = 'nnnn';
var SECRET_KEY = 'nnnn';
var AUTH_TYPE = 'HMAC-SHA1';

var moment = require('moment')    
/* Uncomment out lines below to use your test for getting correct formatted time and date */  
/* var timestamp = moment().format("ddd, DD MMM YYYY HH:mm:ss ZZ");
pm.environment.set("timestampHeader",timestamp);*/
var timestamp = "Tue, 21 Aug 2012 17:29:18 -0000";  /* Only for example */
pm.environment.set("timestampHeader",timestamp);

var hostname = "nnnn-xxxxxxxx.duosecurity.com";
var apicall = "/accounts/v1/account/list"
var body = "realname=First%20Last&username=root"
     
var requestData =  timestamp+"\n"+"POST"+"\n"+hostname+"\n"+apicall+"\n"+body;
console.log(requestData);

var hmacDigest = CryptoJS.HmacSHA1(requestData, SECRET_KEY);
console.log(hmacDigest);
var prebase = CLIENT_KEY+":"+hmacDigest;
console.log(prebase);
var baseComplete = btoa(prebase);
console.log(baseComplete);

var authHeader = "Basic "+baseComplete;
return authHeader;

}
postman.setEnvironmentVariable(‘hmacAuthHeader’, getAuthHeader(request[‘method’], request[‘url’], request[‘data’]));

I used the above Pre Request Script, defined the environment variables. Still getting 40101.

Thank you xrave, finally worked. The problem was that I was selecting “Basic Auth” under Authorization. It should be “No Auth” selected. I used script from above, using method as GET.

hi ,

i have given below script in pre request script and getting an SyntaxError: Invalid or unexpected token,please help where i am doing wrong

function getAuthHeader(httpMethod, requestUrl, requestBody) {

console.log(requestBody);

 console.log(httpMethod); // http type: POST, GET, ETC*/

var CLIENT_KEY = ‘xxxx’;

var SECRET_KEY = ‘xxxxx’;

var AUTH_TYPE = ‘HMAC-SHA1’;

var moment = require(‘moment’)

/*Uncomment out lines below to use your test for getting correct formatted time and date */

var timestamp = moment().format(“ddd, DD MMM YYYY HH:mm:ss ZZ”);

pm.environment.set(“timestampHeader”,timestamp);

/var timestamp = “Tue, 21 Aug 2012 17:29:18 -0000”; / Only for example */

/pm.environment.set(“timestampHeader”,timestamp);/

var hostname = “api-xxxxxxxx

.duosecurity.com”;

var apicall = “/admin/v1/users”

var body = "realname=testingggggg&username=root"

var requestData = timestamp+"\n"+“POST”+"\n"+hostname+"\n"+apicall+"\n"+body;

console.log(requestData);

var hmacDigest = CryptoJS.HmacSHA1(requestData, SECRET_KEY);

console.log(hmacDigest);

var prebase = CLIENT_KEY+":"+hmacDigest;

console.log(prebase);

var baseComplete = btoa(prebase);

console.log(baseComplete);

var authHeader = "Basic "+baseComplete;

return authHeader;

}

postman.setEnvironmentVariable(‘hmacAuthHeader’, getAuthHeader(request[‘method’], request[‘url’], request[‘data’]));