For a deeper look into our World Check One API, look into:

Overview |  Quickstart |  Documentation |  Downloads

question

Upvotes
Accepted
1 2 2 4

401 Unauthorized error when using C# HttpClient

Hi.

I get an 401 unauthorized error when trying to execute a POST request to the World Check API. I can successfully execute the POST request using Postman. Any help would be appreciated.

My code is as follows:

using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace World_CheckAPITester
{
    using System = global::System;
    class Program
    {
        static async Task Main(string[] args)
        {
            await new Program().UsingHttpClient();
        }

        // Combine the data signature and the API secret key to get the HMAC
        // This is the Microsoft HMACSHA256 code copied from the documentation
        private static string generateAuthHeader(string dataToSign, string apisecret)
        {
            byte[] secretKey = Encoding.UTF8.GetBytes(apisecret);
            HMACSHA256 hmac = new HMACSHA256(secretKey);
            hmac.Initialize();

            byte[] bytes = Encoding.UTF8.GetBytes(dataToSign);
            byte[] rawHmac = hmac.ComputeHash(bytes);
            string hex = BitConverter.ToString(rawHmac).Replace("-", "");
            
            return (Convert.ToBase64String(rawHmac));
        }

        private async Task UsingHttpClient() {

            try
            {
                HttpClient wc1Client = new HttpClient();
                string apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
                string apiSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
                string groupId = "0a3687cf-6c70-198a-9b22-d3fa000009bb";

                string wc1Protocol = "https://";
                string wc1Gatewayhost = "rms-world-check-one-api-pilot.thomsonreuters.com";
                string wc1GatewayUrl = "/v1/";
                string wc1QueryUrl = "cases/screeningRequest";

                string wc1Date = DateTime.UtcNow.ToString("R");

                dynamic postObject = new ExpandoObject();
                postObject.secondaryFields = new string[] { };
                postObject.entityType = "INDIVIDUAL";
                postObject.customFields = new string[] { };
                postObject.groupId = groupId;
                postObject.providerTypes = new string[] { "WATCHLIST" };
                postObject.name = "john smith";

                string postData = JsonConvert.SerializeObject(postObject);
                UTF8Encoding encoding = new UTF8Encoding();
                byte[] byte1 = encoding.GetBytes(postData);

                string dataToSign = "(request-target): post " + wc1GatewayUrl + "cases/screeningRequest\n" +
                "host: " + wc1Gatewayhost + "\n" + // no https only the host name
                "date: " + wc1Date + "\n" + // GMT date as a string
                "content-type: " + "application/json" + "\n" +
                "content-length: " + byte1.Length + "\n" +
                postData;

                string hmac = generateAuthHeader(dataToSign, apiSecret);
                string authorisation = "Signature keyId=\"" + apiKey + "\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"" + hmac + "\"";

                wc1Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                wc1Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic", authorisation);
                wc1Client.DefaultRequestHeaders.Date = DateTime.UtcNow;

                StringContent postContent = new StringContent(JsonConvert.SerializeObject(postObject), Encoding.UTF8, "application/json");

                HttpResponseMessage httpResponse = await wc1Client.PostAsync(wc1Protocol + wc1Gatewayhost + wc1GatewayUrl + "cases/screeningRequest", postContent);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
            }
        }
    }
}



Regards.

world-checkworld-check-oneerrorerror-401http
icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Upvotes
Accepted
4.5k 4 8 8

@gerhardtm,

Thank you for sharing the details.

Firstly, I see that there is a difference of more than 30 seconds between request date and response date timestamp in the headers. The time difference of 30 seconds is acceptable for a successful API call. Can you please adjust the system time to NTP and they re-try?

Secondly, In a POST request the headers would be -

1. Date

2. Authorization

3. Content Length

4. Content Type

From the above headers, it looks like only Date and Authorisation is passed as the request headers. Could you please add the content length and the content type as well in the request headers and then try again?

Please provide us the updated request and response headers along with the request body if you are still getting Error 401 after making the above suggested changes to assist you further.

icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Upvotes
4.5k 4 8 8

@gerhardtm,

Thank you for your query.

Can you please provide us the request headers and the response headers along with the request body of the failed API call to investigate on the cause of Error 401?

icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Upvotes
1 2 2 4

Hi @Prabhjyot.Mandla

The request and the response headers as well as the request body generated by sample code I posted are as follows:

Request headers:
Accept: application/json
Authorization: basic Signature keyId="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",algorithm="hmac-sha256",headers="(request-target) host date content-type content-length",signature="CZZr0aVS0jSJTUH2cUd3aKhoLZPN6wT9cBV/QeA45aY="
Date: Tue, 05 Nov 2019 08:47:34 GMT

Request Body:

{
    "secondaryFields": [],
    "entityType": "INDIVIDUAL",
    "customFields": [],
    "groupId": "0a3687cf-6c70-198a-9b22-d3fa000009bb",
    "providerTypes": [
        "WATCHLIST"
    ],
    "name": "john smith"
}


Response Headers:
X-Application-Context: application
Authorization: WWW-Authenticate: Signature realm="World-Check One API",algorithm="hmac-sha256",headers="(request-target) host date content-type content-length
Transfer-Encoding: chunked
Date: Tue, 05 Nov 2019 08:50:02 GMT
Server: ""

I suspect that the request headers are incorrect which is why I need some guidance on how to set the request headers on a C# HttpClient object

Regards


icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Upvotes
1 2 2 4

Hi @Prabhjyot.Mandla

You said:

"Firstly, I see that there is a difference of more than 30 seconds between request date and response date timestamp in the headers. The time difference of 30 seconds is acceptable for a successful API call. Can you please adjust the system time to NTP and they re-try?"

That I can explain, I have set a breakpoint in my code after creating the Httpclient object where the code execution was paused while I copied the header values, which explains the time difference. Apologies for any confusion that might have caused.


Also, I understand that the Date, Authorization, Content Length and Content Type headers are required, but I'm struggling to set them correctly in my code. Can you please provide a sample of C# code on how to set the request headers on a C# HttpClient object?

Regards.

icon clock
10 |1500

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

@gerhardtm,

I'm afraid, we do not have sample C# code for HttpClient object.