nighthawk
nighthawk

Reputation: 842

Add cookies to dependancy injected HttpClient in Asp.Net Core

how can I add cookies to HttpClient used in injected service (example)? I have created some service ApiService and that service has HttpClient injected just like CatalogService in tutorial. Problem is, in order to communicate with external API, my service and it's HttpClient must have a token which was previously received by another API called IdentityService using LoginService. So, when login is completed in fronted web app:

if (!string.IsNullOrEmpty(user.Token)) {
    ClaimsPrincipal principal = ValidateToken(user.Token);
    if (principal != null) {
        AuthenticationProperties p = new AuthenticationProperties();
        p.ExpiresUtc = DateTime.UtcNow.AddDays(7);
        p.IsPersistent = Persistant;
        p.RedirectUri = Request.Query.ContainsKey("ReturnUrl") ? Request.Query["ReturnUrl"].ToString() : "";

        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, p);

        // those cookies are lost when ApiController is called - new instance of http client replaces this one
        var apiService = HttpContext.RequestServices.GetService(typeof(IApiService));
        var s = apiService as ApiService;
        s.SetClientCookies("token", user.Token);

        return new RedirectToPageResult("../Index");
    }
}

return BadRequest("Invalid token");

SetClientCookies currently looks like this but it isn't working because ApiService constructor gets a new instance of HttpClient:

public class ApiService: IApiService {
    public static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
    private HttpClient _client;
    static string BaseUrl = "https://localhost:44365/api";

    public ApiService(HttpClient client) {
        _client = client;
    }

    public async Task < List < DbTest >> GetAccounts() {
        var httpResponse = await _client.GetAsync(BaseUrl + "/account");

        if (!httpResponse.IsSuccessStatusCode) {
            throw new Exception("Cannot retrieve login");
        }

        var content = await httpResponse.Content.ReadAsStringAsync();
        var accounts = JsonConvert.DeserializeObject < List < DbTest >> (content);

        return accounts;
    }

    public void SetClientCookies(string key, string value) {
        _client.DefaultRequestHeaders.Add("Cookie", key + "=" + value);
    }
}

Result is, when API is called with HttpClient, there is no "token" cookie... Any idea how to change this so ApiService's HttpClient doesn't lose its cookies which are received on login?

Related Startup.cs code is this:

services.AddHttpClient<ILoginService, LoginService>("identityServiceClient");

services.AddHttpClient<IApiService, ApiService>("apiServiceClient")
    .ConfigureHttpClient(client => {
        client.Timeout = TimeSpan.FromSeconds(30);
    }).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {
        CookieContainer = new CookieContainer()
    });

Upvotes: 7

Views: 3183

Answers (1)

Nkosi
Nkosi

Reputation: 247551

A possibility would be to add a cookie container to the service collection

var container = new CookieContainer();
services.AddSingleton(container);

and use that instance when registering the client

services.AddHttpClient<IApiService, ApiService>(client => {
    client.Timeout = TimeSpan.FromSeconds(30);
    client.BaseAddress = new Uri("https://localhost:44365/api").
}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() {
        CookieContainer = container,
        UseCookies = true
});

Now the container can be resolved and manipulated as needed.

var container = HttpContext.RequestServices.GetService<CookieContainer>();
container.Add(BaseUrl, new Cookie("token", user.Token));

Upvotes: 5

Related Questions