Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postman - access full request body (to calculate hash) in pre-request script

I'm trying to recreate C# DelegatingHandler inside PostMan.
I've created pre-request script that calculates auth header value.

Currently my script looks like this:

function S4() {
    return (((1+Math.random())*0x10000)|0).toString(16).substring(1); 
}

function GetNonce() {
    return (S4() + S4() + S4()+ S4() + S4() + S4() + S4()+ S4()).toLowerCase();
}

function GetTimeStamp() {
    var d = new Date();
    return Math.round(d.getTime() / 1000);
}

function getAuthHeader(httpMethod, requestUrl, requestBody) {
    var CLIENT_KEY = postman.getEnvironmentVariable('hmac_user');
    var SECRET_KEY = postman.getEnvironmentVariable('hmac_key');
    var AUTH_TYPE = 'HMAC';

    requestUrl = requestUrl.replace(/{{(\w*)}}/g,function(str,key) {return environment[key]});
    requestUrl = requestUrl.toLowerCase();
    var requestTimeStamp = GetTimeStamp();
    var nonce = GetNonce();
    var bodyHash="";

    if (httpMethod == 'GET' || !requestBody) {
        requestBody = ''; 
    } else {
        var md5 = CryptoJS.MD5(requestBody);
        bodyHash = CryptoJS.enc.Base64.stringify(md5);
    }  

    var signatureRawData = [CLIENT_KEY, requestUrl, httpMethod, requestTimeStamp, nonce, bodyHash].join("");

    var key = CryptoJS.enc.Base64.parse(SECRET_KEY);
    var hash = CryptoJS.HmacSHA512(signatureRawData, key);
    var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);

    var header = [CLIENT_KEY, hashInBase64, nonce, requestTimeStamp].join(":");

    return AUTH_TYPE+" "+header;
}

postman.setEnvironmentVariable('hmacAuthHeader', getAuthHeader(request.method, request.url, request.data));

This works perfect for GET requests that don't have any body. However when I send x-www-form-urlencoded request I get unauthorized response (401), because of body hash differences inside C# and Postman.

Inside Postman request.data is a JSON object, but when I investigate request in Fiddler I see it is send as string (see below screenshot) enter image description here

Same thing happens when I send form-data. Inside Postman I've added 3 fields, one with string value, two with files. In Fiddler I can see full request, but inside Postman I can't access those files (see below screenshot) enter image description here

I'm trying to access full request body, because I need to calculate hash out of it.

I have working code in C#, not I want to recreate same requests with Postman.

My question is:
How can I access full request body in pre-request script?

I use this code in C# and it works fine:

internal class HmacClientHandler : DelegatingHandler
{
    private readonly string _applicationId;
    private readonly string _applicationKey;

    public HmacClientHandler(string appId, string appKey)
    {
        _applicationId = appId;
        _applicationKey = appKey;
    }

    protected override async Task<HttpResponseMessage>SendAsync(HttpRequestMessage request,CancellationToken cancellationToken)
    {
        HttpResponseMessage response = null;
        string url = Uri.EscapeUriString(request.RequestUri.ToString().ToLowerInvariant());
        string methodName = request.Method.Method;

        DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan timeSpan = DateTime.UtcNow - epochStart;
        string requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
        string nonce = Guid.NewGuid().ToString("N");

        string contentBase64String = string.Empty;

        if (request.Content != null)
        {
            byte[] content = await request.Content.ReadAsByteArrayAsync();
            MD5 md5 = MD5.Create();
            byte[] hash = md5.ComputeHash(content);
            contentBase64String = Convert.ToBase64String(hash);
        }

        string authenticationKeyString = string.Format("{0}{1}{2}{3}{4}{5}", _applicationId, url, methodName, requestTimeStamp, nonce, contentBase64String);
        var secretKeyBase64ByteArray = Convert.FromBase64String(_applicationKey);

        using (HMACSHA512 hmac = new HMACSHA512(secretKeyBase64ByteArray))
        {
            byte[] authenticationKeyBytes = Encoding.UTF8.GetBytes(authenticationKeyString);
            byte[] authenticationHash = hmac.ComputeHash(authenticationKeyBytes);
            string hashedBase64String = Convert.ToBase64String(authenticationHash);
            request.Headers.Authorization = new AuthenticationHeaderValue("HMAC", string.Format("{0}:{1}:{2}:{3}", _applicationId, hashedBase64String, nonce, requestTimeStamp));
        }

        response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}
like image 753
Misiu Avatar asked Mar 27 '18 09:03

Misiu


People also ask

How do you use a variable from pre-request script in body Postman?

Open a new request tab and enter https://postman-echo.com/get?var={{my_variable}} as the URL. Hover over the variable name to inspect the variable's value and scope. Select Send and send the request. Inspect the response, which confirms that Postman sent the variable value to the API.

What is the difference between pre-request script and tests in Postman?

Execution order of scripts In Postman, the script execution order for a single request looks like this: A pre-request script associated with a request will execute before the request is sent. A test script associated with a request will execute after the request is sent.

What is the use of pre-request script in Postman?

The pre-request script is the entry point for request execution in Postman. If there is any script/logic added as a part of the pre-request script that gets executed first following which the actual request execution takes place and once the response is received, the tests or the post request scripts get executed.


1 Answers

The previously quoted issue (#1050) appears to only apply to the request body in binary/file mode (RequestBody.MODES.file). The RequestBody API provides what I need for a similar use case: https://www.postmanlabs.com/postman-collection/RequestBody.html

I believe that if you reference pm.request.body in your pre-request script, it will provide what you're looking for. Specifically, pm.request.body.toString() appears to provide the actual x-www-url-encoded string that will ultimately appear in the request (although, if you use environment variables in your request parameters, these will be emitted unresolved, e.g., {{variable_name}}).

So for your script above, I changed the last line to:

postman.setEnvironmentVariable('hmacAuthHeader', getAuthHeader(request.method, request.url, pm.request.body.toString()));

... and that seems to provide a plausible HMAC. Be sure to pay attention to all of the rules for your hash/HMAC protocol, including sorting of parameters, whitespace handling, etc.

Also, I'm not certain that the RequestBody API is available in all incarnations of Postman, but works swimmingly on my native Windows and OS X editions. Hope this helps!

like image 120
Britt Avatar answered Nov 14 '22 23:11

Britt