C# HttpClient authorization header removed after send to server - c#

I wanna send request to external API through the HttpClient from my own API in c#.
I am using dot net 5 and basic authentication.
Here is my code:
var client = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Put, "apiUrl");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var param = JsonConvert.SerializeObject(new
{
param1="",
param2=""
});
requestMessage.Content = new StringContent(param, Encoding.UTF8, "application/json");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes($"{user}:{pass}")));
HttpResponseMessage response = await client.SendAsync(requestMessage);
Usually, I send http request like this.
but now I have some problem.
After line HttpResponseMessage response = await client.SendAsync(requestMessage); authorization header removed from my request header and I got UnAuthorized http error.
I know that when redirection occurs, authorization header removed for security reason. Also I'm not sure about redirect in external API.
I add the HttpClientHandler with AllowAutoRedirect = false to my HttpClient
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false,
};
var client = new HttpClient (handler)
{
BaseAddress = new Uri(baseUrl)
};
Now I got Redirect error 301(Permanently moved).
I decided to test the code in Postman. By default, when I call API in Postman, I got http error 405 method not allowed and error detail like this:
{
"detail": "Method "GET" not allowed."}
External API method is PUT. but here I got GET Error.
I tried many options in postman and finally I find the option in Postman:
When I toggle it on, external API work's properly.
Also I test it with Insomnia and it's work properly.
Does it related to my code or dot net 5 or what other thing in my code or it's related to external API?
If it's related to my code, How can I solve the error?
If error related to external API, why Postman and Insomnia response is ok?
External API has Core Policy for specific domain and I send request from other domain.
All I Know is that the CORS policy applied in browser. not in Postman, Insomnia or C# Code.
What about CORS? Does it related to CORS? if Yes, what shall I do?
I will be grateful for your answer.
Update
I detect WWW-Authenticate: JWT realm="api" in the response header.
What exactly is it? and what shall I do?

I find out the problem. It's really Ridiculous.
When I use URL like www.abc.com/api/something the request gets 301 error and postman sends another request like www.abc.com/api/something/. The difference is just / at the end of request.
I tried new URL in postman and first request got ok.
Also I tried URL in my C# code and again its ok.
But i could not understand why.
Thanks a lot dear #pharaz-fadaei

You are right about the removal of authorization headers after redirects. But keep in mind that this behavior is part of the design of the HttpClient in C#. Postman and Insomnia may have different mechanisms to send the authorization headers on each consecutive request that is caused by redirects. The option that you enabled in Postman will make it use the original HTTP method you specified (PUT) as the HTTP method to send further requests based on the redirect messages (Postman uses GET method by default on requests instructed by redirect messages).
The fact that you see a 301 shows that a redirection is required. You can check Location header value in response.Headers to see the real location and send your requests with the authorization headers to that endpoint directly. If I were you I wouldn't use the new location directly because the original endpoint is what you were given by the authors of the API. Instead I would programmatically send the request to the original endpoint and resend the request on 301 codes to the new Location (use PUT method due to the behavior of Postman) until you get the result. This answer can give you some ideas: https://stackoverflow.com/a/42566541/1539231

Since you see WWW-Authenticate: JWT realm="api" header, the external API is required a JWT token authentication, not basic authentication. I think first you might need to check external api's documentation.

Related

Request Rest API with Http failed with 401 but Https works

Below codes worked when the Uri = "https://someRestApi", but reports 401 Unauthorized when Uri = "http://SomeRestApi", why is it happening? The server is hosted in Azure App Service, with
TLS/SSL settings . Sending the http request with Postman does work.
public static void Main(string[] args)
{
var uri = new Uri("http://someTestApiUri.com");
var token = GetTestToken();
HttpWebRequest httpRequest = HttpWebRequest.Create(uri) as HttpWebRequest;
httpRequest.Headers["Authorization"] = "Bearer " + token.AccessToken;
using (HttpWebResponse response = httpRequest.GetResponse() as HttpWebResponse)
{
//Some logic here...
}
}
This maybe because the developer of the API may have specified that endpoint allows only https either through the CORS and origin specification or Attribute Filter. by decorating the ApiController to allow only https.
kindly read some of the articles below.
https://jonathancrozier.com/blog/how-to-configure-your-asp-net-web-apps-and-apis-to-require-https
https://www.c-sharpcorner.com/article/how-to-enable-https-in-asp-net-web-api/
there are many articles that will give you a clue but these are just few so it is not exhaustive.
so if the backend or whoever developed the endpoint specifies that only https endpoint should be hit, when you attempt to hit the http he may return the message or http statusCode 401 . again read more about http status code and what they mean.
Also, it may work in postman because you disabled a security settings on your postman. please try and use a secure channel when making API calls.
Thanks.
Regards
Authentication and authorization in Azure App Service and Azure Functions in the section about disabling requireHttps clearly states:
However, we do recommend sticking with HTTPS, and you should ensure no security tokens ever get transmitted over non-secure HTTP connections..

Why am I getting a 401 unauthorized when using HttpResponseMessage with a secure url?

I have this proprietary code I am working on for my job.
I am writing test cases for it because the code was changed and the test cases are now broken.
It is a C# web Api MVC .Net Framework app
I have a method that I enter a string url in
Then this code executes
HttpResponseMessage response = await Client.GetAsync(url).ConfigureAwait(true);
System.Uri uri = new System.Uri(url); // convert string to Uri
var cert = System.Net.ServicePointManager.FindServicePoint(uri).Certificate;
response.EnsureSuccessStatusCode();
when it gets to response.EnsureSuccessStatusCode() , it gives a 401 unauthorized and then throws an exception not allowing my test to pass
When I try the same thing with http://www.google.com which is not an https, then
it gives a 200. So something is going on with security stuff
What are the things I need to do to get a https to give a 200? Does it need username and password credentials or something or some other token of some sort?
Also, when I test it using Rest Client DHC with the secure https link that was failing above it gives me a 200. However, I had to refresh the bearer token for it to give a 200. If I used an old token it would give a 401.
Furthermoore, when I test a different link like https://www.facebook.com (which is not the one I want to test in my application, just troubleshooting) which is secure, it works giving me a 200 both in my application above and Rest Client DHC even with an old bearer token.
If you're trying to makes CORS requests to web API, you'll need to configure It to accept cross origins requests. If you dont configure your web api to accept cross origin requests, it'll throw these type of errors when calling It. And keep in mind that for web api access, https://www.domain.so and http://www.domain.so are completely diff clients.
Look at this link:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api

C# System.Net.HttpClient intermittent 401 Unauthorized with Github API

I'm using System.Net.Http HttpClient to call Github's rest API.
About 5 out of every 10 requests I make with the HTTP client generated below respond with "401 Unauthorized". I put the same URL and headers in postman and never get any "401 Unauthorized" responses. Clearly, my problem is on the client side.
A reasonable search returned a lot of different advice. The most common was to use an HttpHandler, or webrequest, but it's still not clear why some requests fail. My best guesses were that Github occasionally redirects and the client doesn't handle it well. I saw some references about caching credentials, but those examples all showed "NetworkCredential" object, and but for OAuth we just put the bearer token in the header as shown below, so I'm not sure if that applies to my case.
Can anyone explain the cause of the issue?
public static HttpClient GetClient(string bearerToken)
{
HttpClient client = new HttpClient();
client.BaseAddress = _githubApiUri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_mediaTypeWithQualityHeaderValue));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
client.DefaultRequestHeaders.Add("User-Agent", "System.Net.Http Agent");
return client;
}
It turned out that my clock was 15 seconds fast, making the tokens I was generating invalid because they were "from the future" from Githubs perspective. So, if I paused with a breakpoint, and happened to wait 15 seconds, or more, it would work.

Check url with NTLM on server side

I need to check if url exists and can be reached. In order to do it I send Get request and handle the status:
var httpClient = new HttpClient();
var response = httpClient.GetAsync(new Uri(pageUrl));
isPageAccessible = response.Result.StatusCode == HttpStatusCode.OK;
However, the server uses NTLM for authentication. As I found it here, there are several steps (requests) before I get success OK status. For the first request I get 401 Unauthorized status and can't go to further steps.
All in all, how can I check url on the server with NTML upon completion of all requests?
If you are accessing an authenticated server, you should provide credentials. Credentials of running process for NTLM can be provided with HttpClient as below:
var handler = new HttpClientHandler {
Credentials = System.Net.CredentialCache.DefaultCredentials
};
var httpClient = new HttpClient(handler);
var response = httpClient.GetAsync(new Uri(pageUrl));
You're setting yourself up for failure since there are dozens of reasons why a request may not return a 200 OK response. One may be that the response has no content 204 No Content. Another may be that the request only accepts POST or PUT requests. Another, as you've discovered, may be that it has an authentication system in front of it 401 Not Authorized. Another may be just that the response is a redirect 301 Moved Permanently or 302 Found. Or it could be behind a proxy 305, 306, etc.
The only way you can determine if a URL really exists is to request that the other end prove it. Google does it, Facebook does it, Pinterest does it, etc. The way they do it is they ask the sender to set an MX record in their DNS or a meta tag on their index.html with a custom token they generate. If the token exists, then they're who they say they are.
Anything else is unreliable.

Error 406 Not Acceptable JSON

http://www.jfrog.com/confluence/display/RTF/Artifactory+REST+API#ArtifactoryRESTAPI-CreateorReplaceRepositoryConfiguration
I am using the Create or Replace Repository Configuration call. However I am getting a 406 Error: Not Acceptable. Other PUT calls are working but do not return JSON. I believe JSON is the source of the error but have not been able to resolve or prove this.
I have added the code as below
RestClient Client = new RestClient(uriString);
RestRequest Request = new RestRequest(requestType);
Request.AddHeader("Authorization", "Basic " + credentials);
Request.AddHeader("Accept", "application/json");
I've seen threads where adding the header to accept JSON resolves the error but this has not worked for me.
A 406 HTTP status means that if a web server detects that the data it wants to return is not acceptable to the client, it returns a header containing the 406 error code.
The client can define the characteristics of the data it will accept back from the web server by using the accept headers.
In this case you declare the you would like to accept application/json:
Request.AddHeader("Accept", "application/json");
however the REST API method you are invoking is returning text/plain.
You should change the code to accept text/plain:
Request.AddHeader("Accept", "text/plain");
Wanted to add this for for future users stuck like me. I was having the same issue and tried the request with Postman and saw that the Content-Type was "application/hal+json" I was trying it with application/json without luck.
So running a test in postman I was able to figure out what the server needed exactly.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/hal+json"));
I faced the same 406 Error: Not Acceptable when trying to get JSON on another site. In my case I could see correct JSON when typed url in my browser address field. But downloading it from the same url via my C# code have been producing 406 Error.
None of the answers in this topic solved my problem directly. But at least they pointed out to me that's the point is HTTP headers.
So I googled that page:
https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending
and added all browser headers to my code, and voila! It started to work.
In my case it was enough to fill some data in user-agent header.
First, the Accept header states what the client is ready to get back, not what the client sends.
The header that states what the client sends is Content-Type.
Also, this method does not accept application/json. As clearly stated in the docs, it accepts one of the following:
application/vnd.org.jfrog.artifactory.repositories.LocalRepositoryConfiguration+json
application/vnd.org.jfrog.artifactory.repositories.RemoteRepositoryConfiguration+json
application/vnd.org.jfrog.artifactory.repositories.VirtualRepositoryConfiguration+json

Categories