question

Upvotes
Accepted
16 1 1 3

401 Unauthorised WorldCheck One Zero Footprint

Hi I'm using the sample code for WorldCheck One API (https://developers.refinitiv.com/customer-and-third-party-screening/world-check-one-api/downloads) to try replicate for use on WorldCheck One Zero Footprint. However I'm having 401 unauthorised.

The same code, if I replace the api key/secret & url to that of WorldCheck One, works.

Also I've tested my API secret/key URL & groupId using postman, it works.

The one difference I've found between WorldCheck One API & WorldCheck One Zero Footprint from the Postman predefined configuration is this in the pre-request script:

For WorldCheck One:

var dataToSign = "(request-target): post " + environment["gateway-url"] + "cases/screeningRequest\n" +
        "host: " + environment["gateway-host"] + "\n" +
        "date: " + date + "\n" +
        "content-type: " + environment["content"] +"\n" + 
        "content-length: " + contentLength + "\n" + 
        content;

For WorldCheck One Zero Footprint:

var dataToSign = "(request-target): post " + environment["zfs-gateway-url"] + "cases/screeningRequest\n" +
        "host: " + environment["zfs-gateway-host"] + "\n" +
        "date: " + date + "\n" +
        "content-type: " + environment["content"] +"\n" + 
        "content-length: " + contentLength;

The Zero footprint one doesn't seem to append "content" at the end.

This is the C# code from (https://developers.refinitiv.com/customer-and-third-party-screening/world-check-one-api/downloads) which I'm trying to adapt to use for Zero Footprint. Can anyone point me in the right direction as to where I'm going wrong?

   DateTime dateValue = DateTime.UtcNow; // get the datetime NOW GMT


            string date = dateValue.ToString("R"); // WC1 header requires GMT datetime stamp
            //Console.WriteLine(date);
            //set host and credentials to the WC1 API Pilot server WC1SampleClientAPI account
            string apikey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
            string apisecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
            string gatewayurl = "/v1/";
            string gatewayhost = "zfs-world-check-one-api-pilot.thomsonreuters.com";            string requestendpoint = "https://zfs-world-check-one-api-pilot.thomsonreuters.com/v1/cases/screeningRequest";


            string postData = "{ \"groupId\":\"XXXXXXXXXXXXXXXXXXXXXX\", \"entityType\": \"INDIVIDUAL\", \"providerTypes\": [ \"WATCHLIST\" ], \"name\": \"Smith\", \"secondaryFields\":[] }";


            //Console.WriteLine(postData.Length);


            string msg = postData;
            //Console.WriteLine(msg);
            UTF8Encoding encoding = new UTF8Encoding();
            byte[] byte1 = encoding.GetBytes(postData);


            // Assemble the POST request - NOTE every character including spaces have to be EXACT 
            // for the API server to decode the authorization signature       
            string dataToSign = "(request-target): post " + gatewayurl + "cases/screeningRequest\n" +
                "host: " + gatewayhost + "\n" +   // no https only the host name
                "date: " + date + "\n" +          // GMT date as a string
                "content-type: " + "application/json" + "\n" +
                "content-length: " + byte1.Length + "\n" +
                 msg;


            Console.WriteLine("---api secret---");
            Console.WriteLine(apisecret);
            Console.WriteLine("---dataToSign---");
            Console.WriteLine(dataToSign);
            Console.WriteLine("string hmac = generateAuthHeader(dataToSign, apisecret);");
            // The Request and API secret are now combined and encrypted
            string hmac = generateAuthHeader(dataToSign, apisecret);


            // Assemble the authorization string - This needs to match the dataToSign elements 
            // i.e. requires  host date content-type content-length
            //- NOTE every character including spaces have to be EXACT else decryption will fail with 401 Unauthorized
            string authorisation = "Signature keyId=\"" + apikey + "\",algorithm=\"hmac-sha256\",headers=\"(request-target) host date content-type content-length\",signature=\"" + hmac + "\"";






            Console.WriteLine("---Hmac---");
            Console.WriteLine(hmac);
            //Console.WriteLine(authorisation);


            // Send the Request to the API server
            HttpWebRequest WebReq = (HttpWebRequest)WebRequest.Create(requestendpoint);
            // Set the Headers
            WebReq.Method = "POST";
            WebReq.Headers.Add("Authorization", authorisation);
            WebReq.Headers.Add("Cache-Control", "no-cache");
            WebReq.ContentLength = msg.Length;
            WebReq.Date = dateValue; // use datetime value GMT time
            // Set the content type of the data being posted.
            WebReq.ContentType = "application/json";
            WebReq.ContentLength = byte1.Length;


            Stream newStream = WebReq.GetRequestStream();
            newStream.Write(byte1, 0, byte1.Length);


            // Get the Response - Status OK
            HttpWebResponse WebResp = (HttpWebResponse)WebReq.GetResponse();
            // Status information about the request
            Console.WriteLine(WebResp.StatusCode);
            Console.WriteLine(WebResp.ResponseUri);


            // Get the Response data
            Stream Answer = WebResp.GetResponseStream();
            StreamReader _Answer = new StreamReader(Answer);
            string jsontxt = _Answer.ReadToEnd();


            // convert json text to a pretty printout
            var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsontxt);
            var f = Newtonsoft.Json.JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.Indented);
            Console.WriteLine(f);
            Console.WriteLine("Press any key");
            Console.ReadKey(); // pause for any key
        }


        public 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);
            Console.WriteLine("---rawHmac---");
            string hex = BitConverter.ToString(rawHmac).Replace("-", "");
            Console.WriteLine(hex);
            return (Convert.ToBase64String(rawHmac));
        }
world-checkworld-check-oneworld-check-one-zf
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
2.4k 6 5 6

contentlength.png@yusuf.fauzel

I see that you're passing the request payload as part of the headers in your code (i.e. msg)

pwc.png when you're forming the dataToSign variable, you're adding the payload(msg) along with the content length which shouldn't be the case(see attached pre-request script), can you please pass the request payload as a normal payload and not send it as part of the request headers.

Secondly, I see that you're calculating the string length and then UTF-8 encoding and again calculating its length , I believe calculating it only once after it's encoded will do the job.

Please try by eliminating the payload from the headers and let me know your findings.

Thanks for your patience.


pwc.png (13.2 KiB)
contentlength.png (23.7 KiB)
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
2.4k 6 5 6

@yusuf.fauzel

Hi,

I believe the API key & Secret you have is to access the World-Check One API (which works), but let me help you understand that you cannot use the same keys to access Zero Footprint API, if your product of interest is World-Check One ZFS, please download the postman collection for the ZFS and get in touch with your sales specialist who will be able to get you the trial access for Zero Footprint API instead of World-Check One which explains why you're observing a 401 Unauthorized response.

Regards,

Mehran Khan

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
2.4k 6 5 6

@yusuf.fauzel

Can you please provide me the complete request and response of the 401 Unauthorized case?

Regards,

Mehran Khan

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
16 1 1 3

Hi Mehran,

I already have a different key & secret for World Check One Zero Footprint. I also downloaded the Postman collection for ZFS & it works on Postman.

The issue I'm having is when trying on C#, I'm getting 401 unauthorized, despite using the right credentials for ZFS.

I suspect the issue is with the dataToSign function. I see that on ZFS it doesn't append content at the end. See attached image.


zfs.png (74.7 KiB)
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
2.4k 6 5 6

@yusuf.fauzel

Thanks for the clarification , I am looking into it , I will get back to you shortly.

Regards,

Mehran Khan

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
16 1 1 3

@Mehran.Ahmed Khan

The headers from the C# application (I have masked the start of the groupId):

POST https://zfs-world-check-one-api-pilot.thomsonreuters.com/v1/cases/screeningRequest HTTP/1.1
Authorization: Signature keyId="080d9f9e-2863-462f-a6b6-ccf28d322485",algorithm="hmac-sha256",headers="(request-target) host date content-type content-length",signature="CwByRxCFZ5b52j0fEmxb2t5sCwHzzH+n+ht16s6pvM4="
Cache-Control: no-cache
Date: Wed, 20 Mar 2019 12:36:03 GMT
Content-Type: application/json
Host: zfs-world-check-one-api-pilot.thomsonreuters.com
Content-Length: 153
Expect: 100-continue
Connection: Keep-Alive
{ "groupId":"***********4f7", "entityType": "INDIVIDUAL", "providerTypes": [ "WATCHLIST" ], "name": "Smith", "secondaryFields":[] }

Response:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
Content-Length: 2
Connection: keep-alive
Date: Wed, 20 Mar 2019 12:36:28 GMT
x-amzn-RequestId: c834e04c-4b0c-11e9-b59a-959f6f65742e
x-amzn-ErrorType: AccessDeniedException
x-amz-apigw-id: W10H6F9MjoEFvvw=
X-Cache: Error from cloudfront
Via: 1.1 2a3894d93a2a1e3b94fb6ed07542ad37.cloudfront.net (CloudFront)
X-Amz-Cf-Id: da27aJfDk3-tkXBTBvqXYDSPbpRtYRdvnPOxXWhW_9LyB8HIPT3oYQ==
[]
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
16 1 1 3

@Mehran.Ahmed Khan Any updates re. the above?

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
2.4k 6 5 6

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.

Upvote
16 1 1 3

Hi @Mehran.Ahmed Khan,

Thank you for all your help. I managed to fix the issue with your pointers. The final error which was causing the API to fail was this:

I didn't remove the newline after I removed the payload from the dataToSign.


zfs.png (7.2 KiB)
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.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

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