Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HMAC authentication via Postman

I'm using an example for setting up HMAC authentication for a Web API project. The original example source code/project is available here:

http://bitoftech.net/2014/12/15/secure-asp-net-web-api-using-api-key-authentication-hmac-authentication/

I'm trying to get Postman to construct and send a GET request in it's pre-request script. However the request always fails with a 401 and I can't figure out why.
Postman pre-request script:

var AppId = "4d53bce03ec34c0a911182d4c228ee6c";
var APIKey = "A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc=";
var requestURI = "http%3a%2f%2flocalhost%3a55441%2fapi%2fv1%2fdata";
var requestMethod = "GET";
var requestTimeStamp = "{{$timestamp}}";
var nonce = "1";
var requestContentBase64String = "";

var signatureRawData  = AppId + requestMethod + requestURI + requestTimeStamp +  nonce + requestContentBase64String; //check
var signature = CryptoJS.enc.Utf8.parse(signatureRawData);
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var signatureBytes = CryptoJS.HmacSHA256(signature,secretByteArray)

var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes);
postman.setGlobalVariable("key", "amx " + AppId + ":" + requestSignatureBase64String + ":" + nonce + ":" + requestTimeStamp);
like image 716
Sulphy Avatar asked Jul 23 '18 09:07

Sulphy


2 Answers

This is the code I'm using in my Pre-Script. It works for any query GET, PUT, POST, DELETE.

You need to change the AppId & the APIKey values and on the last line adjust the name of the environment variable "hmacKey" with yours.

function interpolate (value) {
    const {Property} = require('postman-collection');
    return Property.replaceSubstitutions(value, pm.variables.toObject());
}

var uuid = require('uuid');
var moment = require("moment")

var hmacPrefix = "hmac";
var AppId = "4d53bce03ec34c0a911182d4c228ee6c";
var APIKey = "A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc=";
var requestURI = encodeURIComponent(pm.environment.values.substitute(pm.request.url, null, false).toString().toLowerCase());
var requestMethod = pm.request.method;
var requestTimeStamp = moment(new Date().toUTCString()).valueOf() / 1000;
var nonce = uuid.v4();
var requestContentBase64String = "";
var bodyString = interpolate(pm.request.body.toString());

if (bodyString) {
    var md5 = CryptoJS.MD5(bodyString);
    requestContentBase64String = CryptoJS.enc.Base64.stringify(md5);
}

var signatureRawData  = AppId + requestMethod + requestURI + requestTimeStamp +  nonce + requestContentBase64String; //check
var signature = CryptoJS.enc.Utf8.parse(signatureRawData);
var secretByteArray = CryptoJS.enc.Base64.parse(APIKey);
var signatureBytes = CryptoJS.HmacSHA256(signature,secretByteArray);
var requestSignatureBase64String = CryptoJS.enc.Base64.stringify(signatureBytes);

var hmacKey = hmacPrefix + " " + AppId + ":" + requestSignatureBase64String + ":" + nonce + ":" + requestTimeStamp;
postman.setEnvironmentVariable("hmacKey", hmacKey);
like image 153
Florian SANTI Avatar answered Oct 10 '22 10:10

Florian SANTI


After a few days of testing I figured out the problem. It was actually to do with the variable placeholders provided by Postman of all things. In testing the placeholder {{$timestamp}} at face value was passing a valid value. When I stripped the signature back to start with just a single segment I was getting authenticated successfully. Until of course I put the timestamp placeholder back in.

When I swapped out the placeholder for the actual value passed in the header it worked fine. I can only conclude that there must be some extra character I can't see. Perhaps on the Postman side when it creates the signature. The problem extends to other placeholders such as {{$guid}}.

like image 22
Sulphy Avatar answered Oct 10 '22 10:10

Sulphy