@pereiraarun commented on Mon Jun 11 2018
Testing on .NET Core 2.1 (by setting Target Framework 2.1), the following code results in a 403 Forbidden since the header is not set correctly.
Header is set using the following method:
c#
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "9ac..b87b20");
var result = client.GetStringAsync("/api/ipam/prefixes/").Result;
Moving back to .NET Core 2.0 (by setting Target Framework 2.0) fixes the issue. The same happens when using RestSharp. Seems there is some bug with HttpClient and setting authorization headers.
Testing through: Vs Professional 2017 (15.7.3) on Windows 10 with the latest updates.
@brockallen commented on Mon Jun 11 2018
Are you sure the scheme is correct? Normally it should be "Bearer" (not "Token") if you're doing an OAuth2 style client.
@pereiraarun commented on Tue Jun 12 2018
The code works as posted in .Net Core 2.0. The code is used for https://netbox.readthedocs.io/en/latest/api/authentication/
$ curl -H "Authorization: Token d2f763479f703d80de0ec15254237bc651f9cdc0" -H "Accept: application/json; indent=4" http://localhost/api/dcim/sites/
{
"count": 10,
"next": null,
"previous": null,
"results": [...]
}
@Petermarcu, could you provide a code to reproduce the issue?
The code:
```c#
class Program
{
static async Task Main(string[] args)
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://example.com");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", "9ac..b87b20");
var result = await client.GetStringAsync("/api/ipam/prefixes/");
Console.WriteLine(result);
}
}
generates a request with authorization header filled:
GET http://example.com/api/ipam/prefixes/ HTTP/1.1
Authorization: Token 9ac..b87b20
Host: example.com
```
Testing on .NET Core 2.1 (by setting Target Framework 2.1), the following code results in a 403 Forbidden since the header is not set correctly.
@pereiraarun
Is there a way we can repro this problem? I don't see any problem with the APIs that set the 'Authorization' header. Can you provide some traces to show exactly what the headers are being set to if they are being perceived as "not set correctly"?
Sure. Will do when I get a chance. I realize I was being vague with my bug report.
Incidentally, the code posted by @nbalakin above actually works through LinqPad but not with a .net core 2.1 project.
@pereiraarun
We are unable to reproduce the problem. Using the code above generates a request with the right headers. So, perhaps the problem is that server is having issues validating the request headers.
For now, we'll close this issue. If you have repro that we can run to demonstrate that invalid headers are being sent by HttpClient, then we can re-open the issue. Thx.
I have the same issue using 'Bearer'. worked in 2.0, fails no matter what I do in 2.1. I'm forced to roll everything back to 2.0. IMHO Core 2.1 is not ready for prime time. I will be staying away from it for at least the rest of the year.
+1 this issue. Didn't have it it 2.0 but now have it in 2.1.
It seems to work fine when PUTing/POSTing to another .NET Core application. When posting to a .NET Framework (4.6) project the following occurs:
Code:
c#
client.Timeout = new TimeSpan(0, 0, REQUEST_TIMEOUT_S);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token); // I've tried "Token" as well.
var response = await client.PutAsync(Uri, new StringContent(data, Encoding.UTF8, "application/json"));
if (!response.IsSuccessStatusCode)
{
var errorResponse = await response.Content.ReadAsStringAsync();
return false;
}
Server side, I explicitly throw an exception and iterate through the headers. Notice authorization is not even there.
"errorResponse":
Content-Length= 2239, Content-Type= application/json; charset=utf-8, Cookie= ASP.NET_SessionId=<sessionidstring>, Host= mydomain.com, Request-Context= appId=<appidstring>, Request-Id= <requestidstring>
+1 for me. I have also have this issue in this code (which used to work in 2.0):
```c#
client.Timeout = new TimeSpan(0, 0, 120);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", siteSettings.EventBriteOAuthKey);
var responseMessage = await client.GetAsync(url);
var data = await responseMessage.Content.ReadAsStringAsync();
```
The bearer token is not actually added to the request. This issue is occuring when posting to EventBrite's API in this case.
+1 for me on 2.1.403. Forgive the code, I've been trying to track down the issue before running into this thread:
Checking on the code in debug:
Cannot add value because header 'Authorization' does not support multiple values.
```c#
var client = new HttpClient();
var _authHeader = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "username", "password"))));
client.DefaultRequestHeaders.Authorization = _authHeader;
var req = new HttpRequestMessage(HttpMethod.Get, _apiUrl);
req.Headers.Authorization = _authHeader;
// req.Headers.Add("Authorization", $"{_authHeader.Scheme} {_authHeader.Parameter}");
req.Headers.Add("Test", $"{_authHeader.Scheme} {_authHeader.Parameter}");
var res = await client.SendAsync(req);
```
I've gone back and tried the code as outlined in https://github.com/dotnet/corefx/issues/30349#issuecomment-396885353 above, but that did not work either.
@karelz
I had the same problem and found it was related to an automatic redirect. For a temporary fix, I was able to use the URL I was being redirected to instead. It seems like the authentication header is being lost during the redirect.
If you disable AllowAutoRedirect on the HTTP client, can you check if you're being redirected?
c#
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
var client = new HttpClient(handler);
Does anyone have a repro you can share with us, so that we can try it locally?
Aren't redirects expected to drop authentication header? (from security reasons)
Aren't redirects expected to drop authentication header? (from security reasons)
Yes. That behavior is by-design. 'Authorization' request headers are removed during redirects.
Yes. That behavior is by-design. 'Authorization' request headers are removed during redirects.
There are ways to preserve them though. That requires using a CredentialsCache
object and populating it with credentials assigned to specific Uri paths. Then, assign that object to the HttpClientHandler.Credentials property.
However, manually adding 'Authorization' request headers is not a recommended pattern anyways. And those headers will be removed during redirects.
FYI: 2 weeks ago we released a security fix to remove Authorization
request headers from redirects. See dotnet/corefx#32730.
.NET Core 2.0 didn't get the patch because it is out of support as of 10/1.
If anyone hits the problem without redirects being involved, please let us know. That is something we would look into. We would need repro or further details in such case to make progress.
Thanks all, the security change about removing Authorization headers is in fact what was going on in my case. For those still working through it, here's the code I have - working now:
Adapted from: https://stackoverflow.com/a/28671822/5043701
```c#
private async Task
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = authHeader;
var response = await client.GetAsync(url);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// Authorization header has been set, but the server reports that it is missing.
// It was probably stripped out due to a redirect.
var finalRequestUri = response.RequestMessage.RequestUri; // contains the final location after following the redirect.
if (finalRequestUri != url) // detect that a redirect actually did occur.
{
// If this is public facing, add tests here to determine if Url should be trusted
response = await client.GetAsync(finalRequestUri);
}
}
return await response.Content.ReadAsStringAsync();
}
```
@karelz , I understand why the security fix was added, but doesn't this raise another important issue? What if there is some other sensitive header included in the original request. Won't that get sent as part of the redirect?
Shouldn't there be a callback on HttpClient or the HttpClientHandler that exposes the headers so that we can add or remove them as necessary?
Are we meant to write handler code on every http call that may redirect as @chrisipeters has demonstrated? That's very onerous and only deals with the problem after the fact.
PS: This has probably been going on since the early versions of HttpClient / HttpClientHandler and probably has implications for all the different platforms. I think I'm experiencing headers being stripped because of redirects in .NET 4.5. What is Microsoft's recommended approach to this, and are there long term plans to add a callback to that this problem can be dealt with in a graceful way?
@MelbourneDeveloper I believe Microsoft's official solution for this at the moment of writing this comment (found on MSDN) is to write your own authentication module, which is not ideal. Our request to a url has a redirect that changes every year, sometimes more than once so it's unreasonable to use CredentialsCache
for our use case.
In my opinion, an option should just be added to not remove headers on redirect.
Most helpful comment
I had the same problem and found it was related to an automatic redirect. For a temporary fix, I was able to use the URL I was being redirected to instead. It seems like the authentication header is being lost during the redirect.
If you disable AllowAutoRedirect on the HTTP client, can you check if you're being redirected?
c# var handler = new HttpClientHandler() { AllowAutoRedirect = false }; var client = new HttpClient(handler);