You can create an authentication delegating handler like this:
public class AuthenticationHttpMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// Get the token or other type of credentials here
// string scheme = ... // E.g. "Bearer", "Basic" etc.
// string credentials = ... // E.g. formatted token, user/password etc.
request.Headers.Authorization =
new AuthenticationHeaderValue(scheme, credentials);
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
then add it to the HttpClient builder and to the DI container:
services
.AddTransient<AuthenticationHttpMessageHandler>()
.AddHttpClient("MyClient")
.AddHttpMessageHandler<AuthenticationHttpMessageHandler>();
then use IHttpClientFactory to create HttpClient instances.
The core advantage of this approach is that you clearly separate concerns. You don't touch the primary handler, you don't manage client creation manually, you utilize the whole power of the factory and its builder extension methods. The authentication handler is naturally injected in the pipeline and adds authorization to each request. This handler can be enhanced further by abstracting away the source of credentials and make the handler depend on some IAuthenticationProvider abstraction, which will require only DI configuration and not touching the HttpClient configuration code.