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)
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)
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;
}
}
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.
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.
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With