Hi there,
Currently I am trying to integrate your API to our system.
I keep getting the same error 401 from my code. Postman works though.
I use fillder to catch the request send by postman and visual studio. For the authorization part they looks exactly the same except the hashed part.
POST /v1/cases/screeningRequest HTTP/1.1 Date: Thu, 23 May 2019 17:30:21 GMT Content-Type: application/json Authorization: Signature keyId="x",algorithm="hmac-sha256",headers="(request-target) host date content-type content-length",signature="jMx3FJvGsR2A2Q23fk90ihCwou3u00LHFwejYoxa1+o=" Content-Length: 150 User-Agent: PostmanRuntime/7.13.0 Accept: */* Cache-Control: no-cache Postman-Token: 736fcfdd-3fd7-45d1-93fc-b74bdfb55552 Host: zfs-world-check-one-api-pilot.thomsonreuters.com accept-encoding: gzip, deflate Connection: close POST /v1/cases/screeningRequest HTTP/1.1 Date: Thu, 23 May 2019 17:37:29 GMT Authorization: Signature keyId="x",algorithm="hmac-sha256",headers="(request-target) host date content-type content-length",signature="6jslfdOjpdkwdjbc7CdjE1QMQig30Tmu4TFUmS3j/CE=" Accept: */* Cache-Control: no-cache Content-Type: application/json; charset=utf-8 Host: zfs-world-check-one-api-pilot.thomsonreuters.com Content-Length: 150 Expect: 100-continue Connection: Keep-Alive
I also tried to hard code the time to match the one send by postman, then I got exactly the same hashed value. So I suppose the hash function is ok as well?
If so, why I keep getting 401 error? Can you help me to take a look my code? Thanks
public async Task<WorldCheckOneClientResponse> VerifyAsync(WorldCheckOneClientRequest request, CancellationToken cancellationToken) { var currentDate = DateTime.UtcNow; var requestToSent = "{\"groupId\":\"xxxxxxxxxxxxxxxx\",\"entityType\": \"INDIVIDUAL\",\"providerTypes\": [\"WATCHLIST\"],\"name\": \"john smith\",\"secondaryFields\":[]}"; UTF8Encoding encoding = new UTF8Encoding(); var contentLength = encoding.GetBytes(requestToSent).Length; using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, request.EndpointUrl)) { using (var stringContent = new StringContent(requestToSent, Encoding.UTF8, "application/json")) { httpRequest.Headers.Date = currentDate; httpRequest.Content = stringContent; httpRequest.Headers.Add("Authorization", getAuthorizationHeader(getDataToSign(request.EndpointUrl, currentDate, contentLength), request.ApiSecret, request.ApiKey)); httpRequest.Headers.Add("Accept","*/*"); httpRequest.Headers.Add("Cache-Control", "no-cache"); using (var response = await _httpClient.SendAsync(httpRequest, cancellationToken)) { var clientResponse = await response.Content.ReadAsStringAsync(); OnResponseReceived(clientResponse); if (response.IsSuccessStatusCode) { return JsonConvert.DeserializeObject<WorldCheckOneClientResponse>(clientResponse); } return new WorldCheckOneClientResponse { HasError = true, ErrorCode = response.StatusCode, }; } } } } private string getDataToSign(Uri endpointUri, DateTime dateTime, int contentLength) { var dataToSign = "(request-target): post " + "/v1/" + "cases/screeningRequest\n" + "host: " + endpointUri.Host + "\n" + "date: " + dateTime.ToString("R") + "\n" + "content-type: " + "application/json" +"\n" + "content-length: " + contentLength; return dataToSign; } private string getAuthorizationHeader(string message, string secret, string apiKey) { secret = secret ?? ""; var encoding = new System.Text.ASCIIEncoding(); byte[] keyByte = encoding.GetBytes(secret); byte[] messageBytes = encoding.GetBytes(message); using (var hmacsha256 = new HMACSHA256(keyByte)) { var hashmessage = hmacsha256.ComputeHash(messageBytes); var hmc = Convert.ToBase64String(hashmessage); var returnValue = "Signature keyId=\"" + apiKey + "\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"" + hmc + "\""; return returnValue; } }
Request you to provide us the response headers (in the below format) that you are sending to ZFS server so that I can assist you.
Date:
Authorization:
content-type:
content-length:
Also, please provide us the dataToSign value that you are passing to HMAC function and the JSON payload being sent (in the correct format so that I can calculate the content length of it at my end).
Lastly, I would need the response headers we are sending after the sync screening API call is used.
As soon as I have the above information, I will further debug the reason for 401.
@gaurav.thakur you can mask the API key when you provide us the requested information
@gaurav.thakur Following up to see if you were able to resolve the issue. If yes, kindly share with us, how were you able to fix the 401 error?
Hi Irfan, It was due to setting the Charset to "UTF-8" in the header.
Removing it resolved the problem.
@Irfan.Khan: The pilot environment has been working fine for us, however, we are now getting a 401 against our live credentials with the exact same request structure. Could you please look into it? I have sent you the details in an email. It could be a permission issue. Thanks!
Somehow the code was not post correctly
Here is the formatted one:
public async Task<WorldCheckOneClientResponse> VerifyAsync(WorldCheckOneClientRequest request, CancellationToken cancellationToken) { var currentDate = DateTime.UtcNow; var requestToSent = "{\"groupId\":\"xxxxxxxxxxxxxxxx\",\"entityType\": \"INDIVIDUAL\",\"providerTypes\": [\"WATCHLIST\"],\"name\": \"john smith\",\"secondaryFields\":[]}"; UTF8Encoding encoding = new UTF8Encoding(); var contentLength = encoding.GetBytes(requestToSent).Length; using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, request.EndpointUrl)) { using (var stringContent = new StringContent(requestToSent, Encoding.UTF8, "application/json")) { httpRequest.Headers.Date = currentDate; httpRequest.Content = stringContent; httpRequest.Headers.Add("Authorization", getAuthorizationHeader(getDataToSign(request.EndpointUrl, currentDate, contentLength), request.ApiSecret, request.ApiKey)); httpRequest.Headers.Add("Accept","*/*"); httpRequest.Headers.Add("Cache-Control", "no-cache"); using (var response = await _httpClient.SendAsync(httpRequest, cancellationToken)) { var clientResponse = await response.Content.ReadAsStringAsync(); OnResponseReceived(clientResponse); if (response.IsSuccessStatusCode) { return JsonConvert.DeserializeObject<WorldCheckOneClientResponse>(clientResponse); } return new WorldCheckOneClientResponse { HasError = true, ErrorCode = response.StatusCode, }; } } } } private string getDataToSign(Uri endpointUri, DateTime dateTime, int contentLength) { var dataToSign = "(request-target): post " + "/v1/" + "cases/screeningRequest\n" + "host: " + endpointUri.Host + "\n" + "date: " + dateTime.ToString("R") + "\n" + "content-type: " + "application/json" +"\n" + "content-length: " + contentLength; return dataToSign; } private string getAuthorizationHeader(string message, string secret, string apiKey) { secret = secret ?? ""; var encoding = new System.Text.ASCIIEncoding(); byte[] keyByte = encoding.GetBytes(secret); byte[] messageBytes = encoding.GetBytes(message); using (var hmacsha256 = new HMACSHA256(keyByte)) { var hashmessage = hmacsha256.ComputeHash(messageBytes); var hmc = Convert.ToBase64String(hashmessage); var returnValue = "Signature keyId=\"" + apiKey + "\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"" + hmc + "\""; return returnValue; } }