Is setting the Authorization header in HttpClient safe? - c#

I'm working in a MVC5 ASP.NET project, and learned that to send authenticated requests to a WEB API from the controller I could do the following to add a token to the header(using an example code):
public static class APICaller
{
// Use a single instance for HttpClient to reduce overhead
private static readonly HttpClient client = new HttpClient();
//Set the Authorization Header
public static string SetHeader( string token )
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return("Success");
}
}
Is setting the header this way on the HttpClient thread-safe? Will other users have a way to access this same token, given that there is only one instance of this HttpClient?
EDIT:
I'd like to ask one more question to get a better understanding of how it works. Would I need to add the header each time I'm making a request with the same HttpClient object?

With the approach you have, once you've set the default request header on your static instance, it will remain set without you having to keep setting it. This means that if you have multiple requests coming into your server, you could end up in a situation where the header is set for one user and then changed by another request before that first request makes it out the door.
One option to avoid this would be to use SendAsync when using user-specific authorisation headers. This allows you to tie the header to a specific message, rather than setting it as a default for the HttpClient itself.
The code is a bit more verbose, but would look something like this:
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://path/to/wherever"))
{
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "TheToken");
using (var httpResponseMessage = httpClient.SendAsync(httpRequestMessage))
{
// ...
}
}
As you can see, the header is set specially on each request and therefore the issue of mixing up the headers goes away. The obvious downside is that this syntax is more verbose.

Will other users have a way to access this same token, given that there is only one instance of this HttpClient?
Yes, that is why you need to be careful when setting the default headers.
Would I need to add the header each time I'm making a request with the same HttpClient object?
No, because you set the default header all requests created with that object will have the header.
For things like a Bearer token it is better to not put in the default headers and instead put it in the request header by creating a new HttpRequestMessage object, setting the headers you need there, then using HttpClient.SendAsync( passing in the request message to send the headers along with your request.

Related

Adding Session Request To API Call

I'm unsure how to add the Session Request to the API call as per the Food Hygiene Instructions. I've copied the relevant code and hope I am close, but unsure where to put this one part.
Reference: https://api.ratings.food.gov.uk/help
Need to add into the API call: Session.Request.Headers.Add("x-api-version", 2);
Partial Code:
readonly string Baseurl = "https://api.ratings.food.gov.uk";
public async Task<ActionResult> Index()
{
List<Authorities> AuthInfo = new List<Authorities>();
using var client = new HttpClient
{
//Passing service base url
BaseAddress = new Uri(Baseurl)
};
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
***//Session.Request.Headers.Add("x-api-version", 2);***
//Sending request to find web api REST service resource GETRegions using HttpClient
HttpResponseMessage Res = await client.GetAsync("Authorities/basic");
The documentation you're referring to is misleading/incorrect. When you talk about Session (at least in the .NET world) you talk about the server-side. You're obviously a client of the API, rather than a developer of it, so they asking you to put stuff in the Session is incorrect.
You're a client, passing headers in your requests, so it's just:
client.DefaultRequestHeaders.Add("x-api-version", "2");
Side note, you may want to reuse that HttpClient instance if you are going to make that call often.
Side note 2: you may want to ask them to fix the docs :)

UWP HttpClient disable "If" headers

I found one interesting feature in UWP and HttpClient (it also works with WebRequest) :
Any Http request sends "If-*" headers. I did experiment with UWP and WPF apps. I sent request to Azure file storage which doesn't support "If-" headers and will return Error 400 if headers will be sended. So here is my code:
HttpClient client = new HttpClient();
var response = await client.GetAsync("LINK_TO_AZURE_FILE_STORAGE_IMAGE");
Very simply, similar for two apps. Result - WPF app doesn't send "If-*" headers, UWP does. So It means that I'm not able to use File Storage in UWP apps, I just have Error 400.
My question is - can I disable this st...d caching ? Thanks for your attention
Yeah, while using HttpClient in UWP apps, it will automatically use the local HTTP cache by default. For the first time, you code should work. Then you will get 400 Error as in the first response, it contains cache data and all subsequent requests will use this cache by default like following:
To fix this issue, we can use Windows.Web.Http.HttpClient class with HttpBaseProtocolFilter class and HttpCacheControl class to disable the cache like following:
var filter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
filter.CacheControl.ReadBehavior = Windows.Web.Http.Filters.HttpCacheReadBehavior.MostRecent;
filter.CacheControl.WriteBehavior = Windows.Web.Http.Filters.HttpCacheWriteBehavior.NoCache;
var httpClient = new Windows.Web.Http.HttpClient(filter);
var response = await httpClient.GetAsync(new Uri("LINK_TO_AZURE_FILE_STORAGE_IMAGE"));
To make this method work, we need make sure there is no local HTTP cache. As HttpCacheReadBehavior.MostRecent represents it will still use the local HTTP cache if possible. So we'd better uninstall the app first and do not use HttpClient client = new HttpClient(); in the app.
Update:
Starting from Windows Anniversary Update SDK, there is a new enum value NoCache added to HttpCacheReadBehavior enumeration. With a combination of ReadBehavior and WriteBehavior, we can implement a variety of cache-related behaviors. When we don't want to use local cache, we can just set the ReadBehavior to HttpCacheReadBehavior.NoCache like:
var filter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
filter.CacheControl.ReadBehavior = Windows.Web.Http.Filters.HttpCacheReadBehavior.NoCache;
var httpClient = new Windows.Web.Http.HttpClient(filter);
Have you tried using the Remove method of the DefaultRequestHeaders property of the HttpClient class?

MVC to WebApi : Can a header be compressed?

I have started to get some issues where I am over the limit with my headers.
I have a MVC controller, calling a WebApi Controller/Service.
I know the trigger, it is a saml-token (xml) that I've converted to base64.
No, I don't have control of the SecurityToken service...so JWT is not an option at this time. Trust me, I've raised my concerns several times.
We use the saml to secure the WebApi Controller(s) using a custom delegating handler that reads the custom-header and transforms it to a ClaimPrincipal...
I have seen gzip code examples for dealing with the Response, but after hours of googling, I haven't found if there is a way to compress my custom header (or all of them if that's the only way)...for the ~Request.
Ideally I would be able to compress the
"X-My-Custom-Header"
and deal with uncompressing it on the webapi side....
So I'm at a loss to know if this is even possible. This is the first time I've ever had to deal with a way too big header issue.
Sample MVC code below. As an FYI, the windows-credentials are sent over, but that contains the Identity that runs the AppPool that runs the MVC.
My custom header is the saml that is associated with the specific logged in User. Thus why I need to send it over and consider it separately from the windows-identity.
using (var client = new HttpClient(HttpClientHandlerFactory.GetWindowsAuthenticationHttpClientHandler()))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string base64SerializedToken = "SuperDuperLongBase64String_IMeanSuperDuper";
client.DefaultRequestHeaders.Add("X-My-Custom-Header", base64SerializedToken);
Uri baseUri = new Uri("http:www.mywebapiservice.com");
Uri destinationUri = new Uri(baseUri, "doSomething");
HttpResponseMessage response = client.PostAsJsonAsync(new Uri(new Uri(this._baseUri), destinationUri.ToString()).ToString(), accountName).Result;
if (response.IsSuccessStatusCode)
{
returnItem = response.Content.ReadAsAsync<MyCustomReturnObject>().Result;
}
else
{
string errorMessage = response.Content.ReadAsStringAsync().Result;
throw new InvalidOperationException(errorMessage);
}
}
public static class HttpClientHandlerFactory
{
public static HttpClientHandler GetWindowsAuthenticationHttpClientHandler()
{
HttpClientHandler returnHandler = new HttpClientHandler()
{
UseDefaultCredentials = true,
PreAuthenticate = true
};
return returnHandler;
}
}
Considering that a SAML token can be several kilobytes in size, depending on the number of claims, sending it as a header is probably a bad idea. Even if you can get it to work now there's no guarantee that it will continue to work if the claim count grows.
Since there is no standard for header compression you will have to modify both ends of the conversation to do something about it. That being the case, why not simply add the SAML token as part of the request body in your API?
If that's really not going to fly (I get that project managers are often painful when it comes to things like this) then you'll have to look into using something like GZipStream to pack the XML, but at some point you're still going to run into problems. This is a bandaid, not a solution.
No, there's no standard for header compression in HTTP. This might have something to do with the fact that you'd need to read the headers to know if (and how) the headers are compressed.
If you don't have a way to decompress whatever manual compression you figure out on the other side, you're out of luck.

Explicitly Set Content-Type Headers For Get Operation in HttpClient

Is there a way in which I can explicitly set the Content-Type header values when performing a GET with HttpClient ?
I realise this breaks 1.1 protocol, but I am working with a API that does not conform to it, and REQUIRES I set a Content-Type Header.
I have tried this with to no avail...
using (var httpClient = new HttpClient())
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.com");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded+v1.3");
await httpClient.SendAsync(httpRequestMessage)
}
I've inspected the DefaultRequestHeaders after the TryAddWithoutValidation is added, and it does not seem to be setting the Content-Type value.
If I try to set the Content-Type of the httpRequestMessage (by setting httpRequestMessage.Content = ..., I get the following error:
Cannot send a content-body with this verb-type.
Is there a way that I can explicitly set the Content-Type for a GET operation using the HttpClient?
Based on my findings i concluded the HttpClient is very restrictive in terms of the protocol rules. I also reflected through the implementation DLL and i couldn't find anything that it would indicate that it allows protocol violations.
GET requests shouldn't have content-type headers, and the HttpClient is enforcing that rule.
I think the exception message when you try to set the content-type header is self-descriptive:
System.InvalidOperationException: Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.
Also if you use set the content body you get one more self-descriptive message:
System.Net.ProtocolViolationException: Cannot send a content-body with this verb-type.
Since you are willing to violate HTTP rules for GET requests i am pretty sure your only option is to stick with the less restrictive WebClient, which works in that scenario.
It's possible - and very dirty - to override the library behavior with a bit of reflection and by introducing a DelegatingHandler that you give as argument to the HttpClient constructor. See the code below.
public class HmacAuthenticatingHandler : DelegatingHandler
{
public HmacAuthenticatingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// HACK: Set 'Content-Type' even for GET requests
var invalidHeaders = (HashSet<string>)typeof(HttpHeaders)
// use "_invalidHeaders" for System.Net.Http v2.2+
.GetField("invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(request.Headers);
invalidHeaders.Remove("Content-Type");
request.Headers.Remove("Content-Type");
request.Headers.Add("Content-Type", "application/json");
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Although of no help to you right now, it does look like a future release of the .NET framework may support this type of protocol violation with the addition of an AddWithoutValidation method:
https://msdn.microsoft.com/en-us/library/hh204926
Have you tried adding headers to content header (as apposed as a request header)
see here
I came across the same situation with an API I need to call, and I was able to work around it by setting the content to an empty StringContent:
httpRequestMessage.Content = new StringContent("", Encoding.ASCII, "application/json");
This sends both a Content-Type and a Content-Length header (with value of 0), which the API I'm calling is ok with. This wouldn't work if the API rejects requests with a Content-Length header.
I'm using .NET Core 3.1. It looks like the version used by OP did not support setting the Content property on a GET request.

Modify request headers per request C# HttpClient PCL

I'm currently using the System.Net.Http.HttpClient for cross platform support.
I read that it is not a good practice to instantiate a HttpClient object for each request and that you should reuse it whenever possible.
Now I have a problem while writing a client library for a service. Some API calls need to have a specific header, some MUST not include this specific header.
It seems that I can only manipulate the "DefaultRequestHeaders" which will be send with each request.
Is there an option when actually making the request with e.g. "client.PostAsync()" to modify the headers only for the specific request?
(Info: Requests can be multi threaded).
Thanks in advance!
Yes, you can create a new HttpRequestMessage, set all the properties you need to, and then pass it to SendAsync.
var request = new HttpRequestMessage() {
RequestUri = new Uri("http://example.org"),
Method = HttpMethod.Post,
Content = new StringContent("Here is my content")
}
request.Headers.Accept.Add(...); // Set whatever headers you need to
var response = await client.SendAsync(request);
Use HttpContent.Headers. Simply create HttpContent instance with required headers and pass it to PostAsync method.

Categories