Setup JWT from headers to be used by default in HttpClient

300 Views Asked by At

I use .NET 6 and add HttpClient in Program.cs like this:

builder.Services.AddHttpClient<IUserClient, UserClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Clients:UserBaseUrl"]);
});

In my onion architecture when I want to create an order a request is coming to OrderService.API and to check userId is correct in IUserService from OrderService.BL I call user microservice API with the help of registered HttpClient. So the problem is that for now, I need to transfer JWT to the business logic layer via method parameters.

var createdOrder = await _orderService.Add(model.MapToDto(), HttpContext.Request.Headers["Authorization"]);

I don't like it because, for every method using the HttpClient, it's necessary to provide an extra parameter. I think maybe there is a way to set up HttpClient default authentication during the current request.

I tried to setup default request headers during HttpClient registration:

builder.Services.AddHttpClient<IUserClient, UserClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Clients:UserBaseUrl"]);
    client.DefaultRequestHeaders.Add("Authorization", token); // setup token
});

but I don't know how to get JWT from HttpRequest headers there.

Additionally, I thought maybe I can set up the header for HttpClient in some additional BaseController which would be nested by any other my controller but it doesn't seem to be a great solution. Maybe there is a way for middleware use but as I understand we handle an incoming request to OrderService and can't handle outcoming requests from HttpClient.

So would be grateful for any of your ideas!

1

There are 1 best solutions below

0
Artsiom Auhustsinovich On

Thanks to @Rena who provided a link to an existing similar problem: https://stackoverflow.com/a/62324677/11398810

So I created a message helper like this and it seems work for me:

public sealed class HttpClientsAuthHelper : DelegatingHandler
{
    private readonly IHttpContextAccessor _accessor;

    public HttpClientsAuthHelper(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var token = _accessor.HttpContext.Request.Headers["Authorization"].First();
        request.Headers.Add("Authorization", token);

        return await base.SendAsync(request, cancellationToken);
    }
}

And added these lines to Program.cs:

builder.Services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddTransient<HttpClientsAuthHelper>();

builder.Services.AddHttpClient<IUserClient, UserClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Clients:UserBaseUrl"]);
}).AddHttpMessageHandler<HttpClientsAuthHelper>();

builder.Services.AddHttpClient<IProductClient, ProductClient>(client =>
{
    client.BaseAddress = new Uri(builder.Configuration["Clients:ProductBaseUrl"]);
}).AddHttpMessageHandler<HttpClientsAuthHelper>();

I'm not sure how correct such approach so I'll dive a bit into this logic later =)