5

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?

4

2 回答 2

2

You can put a static function inside AddHeaderPropagation config function:

services.AddHeaderPropagation(options =>
    {
        options.Headers.Add(MyCookie());
    });

And inside MyCookie function, get the current context end extract from them all needed data.

If you need something from your current flow or current controller you can add it inside httpSession and take it inside MyCookie function.

于 2020-07-01T14:08:56.383 回答
2

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.

于 2020-07-02T12:49:15.870 回答