0

I am trying to create an authenticated request using HMAC. I calculate the HMAC in the pre-request script and everything works ok when using a static request body. However, if I use variables within the request body it breaks because the script is reading the request body with the variable tags in it rather than the final rendered body.

I found this question which is brilliant and does a good job of explaining how to use replaceSubstitutions() to determine the rendered body. Problem is, my HMAC is failing authentication in inexplicable ways.

In my script, I calculate the rendered body and log it, then calculate the HMAAC and log that too:

function getRenderedBody() {
    const {Property} = require('postman-collection');
    return Property.replaceSubstitutions(pm.request.body.toString(), pm.variables.toObject());
}
const body = getRenderedBody();
console.log("rendered body: ", body);

// generate hash and digest header
var hash = CryptoJS.HmacSHA256(body, "[my secret here]");
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
pm.environment.set("Digest", hashInBase64);
console.log("hashInBase64: ", hashInBase64);

If I run that, the log shows me a rendered body and a hash, which fails with a 401. If I then take the rendered body from the log and use that as the static body, the new log shows the same hash, but the request succeeds with a 200.

The only logical explanation for that is that what Postman actually sends as the request body is not what my script is saying the rendered body is. And indeed, that's what's happening. Looking at the Console, you can see that what the script thinks the rendered body is is different from what is sent. It's almost like the variable substitution is happening twice.

enter image description here

I'm wondering it it's related to the fact that I am using the built-in fake data variables like $randomInt which generate a new value every time they are called. So when I call pm.variables.toObject(), it calls them once to generate one rendered body, but then they are called again within the body itself. Maybe it would work if I created custom variables within the script, before calling the getRenderedBody() function.

T Nguyen
  • 3,309
  • 2
  • 31
  • 48

1 Answers1

0

As I suspected, it was because I was using the dynamic variables. I refactored the script, and moved all the dynamic variables into it, creating local request variables. And since the local variables were static once set, the call to pm.variables.toObject() worked.

// setup random variables
const countries = ['SG','CN','IN','GB','ES','CA','US','RO','MY'];
pm.variables.set ("amount", _.random (5000,100000));
pm.variables.set ("country", countries[Math.floor(Math.random()*countries.length)]);
pm.variables.set ("senderFirstName", pm.variables.replaceIn('{{$randomFirstName}}'));
pm.variables.set ("senderLastName", pm.variables.replaceIn('{{$randomLastName}}'));
pm.variables.set ("senderEmail", pm.variables.replaceIn('{{senderFirstName}}.{{senderLastName}}@{{$randomDomainName}}'));
pm.variables.set ("senderAddress1", pm.variables.replaceIn('{{$randomInt}} {{$randomStreetName}}'));
pm.variables.set ("senderCity", pm.variables.replaceIn('{{$randomCity}}'));
pm.variables.set ("senderState", pm.variables.replaceIn('{{$randomCity}}'));
pm.variables.set ("senderZip", _.random (10000,99999));
pm.variables.set ("senderPhone", pm.variables.replaceIn('{{$randomPhoneNumber}}'));

pm.variables.set ("studentFirstName", pm.variables.replaceIn('{{$randomFirstName}}'));
pm.variables.set ("studentLastName", pm.variables.replaceIn('{{$randomLastName}}'));
pm.variables.set ("email", pm.variables.replaceIn('{{studentFirstName}}.{{studentLastName}}@{{$randomDomainName}}'));
pm.variables.set ("callbackId", pm.variables.replaceIn('abc{{$randomInt}}'));
pm.variables.set ("studentId", pm.variables.replaceIn('S{{$randomBankAccount}}'));

function getRenderedBody() {
    const {Property} = require('postman-collection');
    return Property.replaceSubstitutions(pm.request.body.toString(), pm.variables.toObject());
}
const body = getRenderedBody();
console.log("rendered body: ", body);

// generate hash and digest header
var hash = CryptoJS.HmacSHA256(body, "[my secret here]");
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
pm.environment.set("Digest", hashInBase64);
console.log("hashInBase64: ", hashInBase64);

The request body, using only local variables:

{
    "provider": "XYZ",
    "payment_destination": "sample",
    "amount": "{{amount}}",
    "country": "{{country}}",
    "sender_first_name": "{{senderFirstName}}",
    "sender_last_name": "{{senderLastName}}",
    "sender_address1": "{{senderAddress1}}",
    "sender_city": "{{senderCity}}",
    "sender_state": "{{senderState}}",
    "sender_zip": "{{senderZip}}",
    "sender_phone": "{{senderPhone}}",
    "sender_email": "{{senderEmail}}",
    "dynamic_fields": {
        "student_id": "{{studentId}}",
        "student_first_name": "{{studentFirstName}}",
        "student_last_name": "{{studentLastName}}",
        "student_email": "{{email}}"
    }
}
T Nguyen
  • 3,309
  • 2
  • 31
  • 48