Error on my HttpClient.DefaultRequestHeaders.Authorization, works on WebClient - http-headers

In my code using WebClient I can set the Authorization as:
var client = new System.Net.WebClient();
client.Headers.Add("Authorization", $"SharedKey {storage_account_name}:{signed_string}");
that works. But for HttpClient:
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"SharedKey {storage_account_name}:{signed_string}");
I get:
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Error>
<Code>AuthenticationFailed</Code>
<Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:81dc1c79-901e-0053-5a16-637a77000000\nTime:2022-05-08T20:04:58.6215674Z</Message>
<AuthenticationErrorDetail>The MAC signature found in the HTTP request 'dpN6L5keMd9beEO/KLD65nuU7VMB6K5UTYtpBemPOpQ=' is not the same as any computed signature. Server used following string to sign: '*****'.</AuthenticationErrorDetail>
</Error>
Note: The ***** is replacing the actual string signed.
I also tried various combinations of explicitly creating the AuthenticationHeaderValue() - same problem. What do I need to do to get it to connect, like it does for WebClient?
Also posted to MSDN

Related

Trustpilot Api - Get Private Product Review always returns Unauthorised response status

I hope someone can help me.
I am trying to retrieve a Product Review for a product using the Trustpilot Api's and am having some success but not getting the results I would expect.
The approach I have taken is as follows:
Get an OAUTH2 token - (Returns a successful response)
Retrieve my business units from a config file and for each business unit get the product reviews using the endpoint: https://api.trustpilot.com/v1/private/business-units/{business-unit}/review?token={OAUTH2 token from step 1} - (Returns a successful response)
For each product review I attempt to retrieve the product review detail. For this I have a couple of options.
(i) Each product review has meta-links and so I can get the product review using the corresponding meta-link and tagging the apikey on e.g. https://api.trustpilot.com/v1/reviews/1234567890abcdefg?apikey={apikey} where the apikey is the one provided up when I registered for a developer account - (Returns a successful response)
(ii) Call the endpoint as documented in the developers.trustpilot.api website (https://developers.trustpilot.com/product-reviews-api#get-private-product-review) : https://api.trustpilot.com/v1/private/product-reviews/{reviewId} - (Returns an Unauthorised status code)
For option (ii) above I have tried multiple ways of passing the apikey (according to the documentation, the endpoint requires the apikey as authorisation.
I am using C# as the language for accessing the Trustpilot apis so the following snippets are how I have tried to call the method.
Set the GetProductReview endpoint as follows:
var url = $"https://api.trustpilot.com/v1/private/product-reviews/" + review.Id.ToString();
using (var client = new HttpClient())
{
var uri = new Uri(url, UriKind.Absolute);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new system.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("apikey", apiKey);
try
{
var response = client.GetAsync(uri).Result;
.
.
.
In the above code snippet, the apikey is passed in to the method and passed to the endpoint as a RequestHeader value.
Set the endpoint as follows:
var url = $"https://api.trustpilot.com/v1/private/product-reviews/" + review.Id + $"?apikey={apiKey}";
and call the HttpClient as follows:
using (var client = new HttpClient())
{
var uri = new Uri(url, UriKind.Absolute);
client.BaseAddress = uri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
try
{
var response = client.GetAsync(uri).Result;
.
.
.
In both cases I receive an HttpStatus 401 - Unauthorized.
The documentation indicates that I should pass the apikey (which I have done in two different ways).
I have also tried calling the endpoint replacing the ?apikey={apiKey} with ?token={token} in case the documentation is incorrect and requires a token.
Additionally, I have also tried passing the token as a RequestHeader value and receieve the same result (Unauthoirised)
I would really like to use the endpoint:
https://api.trustpilot.com/v1/private/product-reviews/{review}
as this returns more information (for example the sku which would allow me to get access back to the product).
Can anyone please tell me where I am going wrong here?
Thanks in advance
The documentation for the /v1/private/product-reviews/{reviewId} endpoint is indeed incorrect, since it actually requires a Business user OAuth Token instead of an API Key.
In this case, you have two options (and the first one you have used before for the /v1/private/business-units/{businessUnitId}/reviews endpoint):
You can pass the access token in the query string: /v1/private/product-reviews/{reviewId}?token={token}. You mentioned you have tried this. Maybe it did not work for you because your token expired before you tried this approach. Can you try again after refreshing the token?
You can also pass the access token as a Bearer authorization header:
var url = $"https://api.trustpilot.com/v1/private/product-reviews/{review.Id.ToString()}";
using (var client = new HttpClient())
{
...
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
...
}
In any case, you caught an error in the documentation that should be fixed soon. As a rule of thumb, all private endpoints (the ones that have /private/ in the path) require a Business user OAuth Token.
EDIT: The documentation for the /v1/private/product-reviews/{reviewId} endpoint has been fixed. Now it shows that a Business user OAuth Token is required.

OAuth 2.0 authentication in RestSharp

I am trying to authenticate RESTful service (sabre REST api) using RESTsharp library but i am not able to authenticate it. I am using my Client id and secret. Please tell me how to authenticate using oAuth 2.0 authenticator.
I have tried this code. ( sabre is using OAuth 2.0 authentication )
public ActionResult Index()
{
var client = new RestClient("https://api.test.sabre.com");
client.Authenticator = new HttpBasicAuthenticator("myclientid", "myclientsecret");
RestRequest request = new RestRequest("/v1/auth/token", Method.POST);
request.AddHeader("Authorization", "Basic " + client);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
IRestResponse response = client.Execute(request);
var content = response.Content;
ViewBag.R = content;
return View();
}
i got this result
{"error":"invalid_client","error_description":"Credentials are missing or the syntax is not correct"}
please tell what i am doing wrong.
Thanks
Snapshot of Fiddler Comparison of Running code (not with RestSharp) and code using RestSharp is shown
With RestSharp
Seems to me like you are adding the Authorization header twice. The documentation here says
The authenticator’s Authenticate method is the very first thing called
upon calling RestClient.Execute
Looking at the implementation of HttpBasicAuthenticator, the Authenticate method adds the appropriate header to the request.
So remove the following line from your example:
request.AddHeader("Authorization", "Basic " + client);
You need to first obtain access token from Sabre that you can later use while making rest api calls.
The access token POST request looks like this:
POST https://api.test.sabre.com/v2/auth/token
Authorization: Basic ZVc5MWNtTnNhV1Z1ZEdsazplVzkxY21Oc2FXVnVkSE5sWTNKbGRBPT0=
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
where the value of Authorization after Basic is the Base64 encoded string based on your clientId and secret
Refer to Sabre Authentication on how this string is created
So, in order to get the access token you just need to send a POST request with required header and request parameters and you do not need to use the Authenticator

Using HttpClient to read encrypted URL on Windows Phone

I was reading a normal URL from my app, and it was working fine with the code:
string returnedTaskTResult = await new HttpClient().GetStringAsync(url);
then I used a new url, this time encrypted, and now is returning the exception:
System.InvalidOperationException: An invalid request URI was provided.
The request URI must either be an absolute URI or BaseAddress must be
set.
Why is this happening and how o fix it?
When you use GetStringAsync, you need to use a absolute URI. It sounds like your encrypted URI is relative. Try this...
var client = new HttpClient() { BaseAddress = new Uri("http://yourhosthere.com");
}
string returnedTaskTResult = await client.GetStringAsync(url);

Can't get a token from the Google API

I'm trying to exchange my authorization code for a token with the Google OAuth2 API for my Windows 8 app, but I keep getting HTTP 400 errors.
This is how I perform the request (simplified):
var url = "https://accounts.google.com/o/oauth2/token";
var body = "code=4/LEXF1iAVRZvfCfdQg9r1aFqoYDgV&client_id=904019870963.apps.googleusercontent.com&client_secret=[removed]&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code";
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.PostAsync(new Uri(url), new StringContent(body));
response.EnsureSuccessStatusCode();
Visual Studio usually simply gives me a HTTP 400 bad request error, when I try the same thing in Fiddler I also get an HTTP 400 error, but with this as content:
21
{
"error" : "invalid_request"
}
0
I read all the documentation about Google OAuth, I searched on Google and StackOverflow for this issue, I tried changing all different sorts of things in my code (UrlEncode, etc.), I used the Google API Playground to see what kind of requests it performs and compared it with my own requests (couldn't find a difference except for the return URL, the auth code and the user-agent). No matter what I do, I just can't get this working, I've been stuck for hours already.
Can anyone help me out here?
Read the body content to get the error json you noticed in Fiddler.
HttpClient httpClient = new HttpClient();
var response = httpClient.PostAsync(new Uri(url), new StringContent(body)).Result;
var content = response.Content.ReadAsStringAsync().Result;
content now holds :
{
"error" : "invalid_request"
}
You could project the error in an object, by specifing your error type like: response.Content.ReadAsAsync().Result
For the invalid request part, you should play with UrlEncode. I know you say you tried it, but applying it at the right spots really solves your problem.
var body = "code="+WebUtility.UrlEncode("4/LEXF1iAVRZvfCfdQg9r1aFqoYDgV")+
"&redirect_uri="+WebUtility.UrlEncode("https://yoursite...")+
"&client_id=904019870963.apps.googleusercontent.com" +
"&scope=" +
"&client_secret=********" +
"&grant_type=authorization_code";
HttpClient httpClient = new HttpClient();
var response = httpClient.PostAsync(new Uri(endpoint),
new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded")).Result;
This is the code that works for me. Should work for you also.

ServiceStack JsonServiceClient send basic instead of Basic for Authorization header

I am using the JsonServiceClient client to talk to a RESTful service hosted on IIS 7 by an external vendor.
Here is some sample code I am using to call their Get method.
ServiceStack.ServiceClient.Web.JsonServiceClient client = new ServiceStack.ServiceClient.Web.JsonServiceClient("UrlToVendor"));
client.SetCredentials("userName", "password");
client.AlwaysSendBasicAuthHeader = true;
DTOReturn result = client.Get<DTOReturn>(string.Empty);
I always get an authorization failure. We put a sniffer and the Authorization header is being sent as:
basic userName:password
instead of
Basic userName:password
We were able to use standard .Net calls to get it to work
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(
"UrlToVendor");
string authInfo = "userName:password";
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
req.Accept = "application/json"; //text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
req.PreAuthenticate = true;
req.Method = "GET";
req.Headers["Authorization"] = string.Format("Basic {0}", authInfo);
System.Net.HttpWebResponse res = (System.Net.HttpWebResponse)req.GetResponse();
And these standard calls failed the same as the JasonServiceClient if we changed "Basic" to "basic".
Any suggestions?
Looks like someone had the same problem. This recent commit changed the auth-scheme from "basic" to "Basic". https://github.com/ServiceStack/ServiceStack/commit/d4f21c5355ab87d7315e142372eef9a40e096b5f
You should be able to just update your dlls.
According to RFC 2617 sec 1.2 the auth-scheme is case-insensitive.
See https://www.rfc-editor.org/rfc/rfc1945#page-47. I would be curious as to why the vendor service won't accept it.