Background
I'm working on an ASP.NET Core Web API where we within our API call a 3rd party API. This 3rd party API requires every request to contain a cookie with an access token. Our API gets this token from a claim (from the ClaimsPrincipal
of the user associated with the request).
Details
This answer shows how to set a cookie on a request, but the example requires that one manually constructs the HttpClient
(to be able to inject a HttpClientHandler
with a CookieContainer
). And since it is not desirable to do manual instantiation of the HttpClient
, I would rather get the HttpClient
injected through DI, which this example illustrates (usin IHttpClientFactory
).
In my service, I have access to a an injected HttpClient
instance (_httpClient
), and the most simple function looks like this:
public async Task<string> GetStatusAsync(ClaimsPrincipal user)
{
// TODO: Add cookie with access token (from user's claims) before making request
return await _httpClient.GetStringAsync(Endpoints.Status);
}
This GitHub issue asks about how to include authorization cookie when using IHttpClientFactory, and the answer says to use the header propagation middleware. And now to my issue:
From what I can see, you set up the header propagation middleware (with all headers and cookies and whatnot) during service configuration at application startup. In our API however, I do not have the value for the authentication cookie before actually making the request to the 3rd party API.
Question
How can I add a cookie to the request on a HttpClient
instance that is injected to my service using IHttpClientFactory
, right before making the actual request?
The solution was indeed to use header propagation. I just had to get all the pieces together correctly.
Header propagation is configured inside ConfigureServices
(in Startup.cs
). And since I want to use data from the current user's claims, the trick is, when adding the cookie header, to use on of the overloads that takes in a function that gives you access to the current context:
services.AddHeaderPropagation(options =>
{
options.Headers.Add("Cookie", context =>
{
var accessToken = context.HttpContext.User.Claims.FirstOrDefault(c => c.Type == "access-token")?.Value;
return accessToken != null ? new StringValues($"token={accessToken}") : new StringValues();
});
});
In addition to this, since I'm using a Typed Service, I also had to configure that that specific service should forward the cookie header (at the same place inside ConfigureServices
in Startup.cs
):
services.AddHttpClient<IApiService, ApiService>(httpClient =>
{
httpClient.BaseAddress = new Uri("https://httpbin.org/");
}).AddHeaderPropagation(options =>
{
options.Headers.Add("Cookie");
});
And finally, the thing that caused me some trouble for a little while: Since I'm using data from the current users claims, the registration of the header propagation middleware (app.UseHeaderPropagation();
inside Configure
in Startup.cs
) must happen after adding the authentication middleware (app.UseAuthentication();
). If not, the claims haven't been set yet.
And as a final tip: I head great us in https://httpbin.org/ when working on this. The request inspection endpoints there are really useful to see what data you actually pass along in your request.