Enterprises continue to move from big monoliths to a microservice oriented architecture. Of course, this has lots of advantages over having one or more big monoliths in terms of scalability, maintainability and so forth. When moving to a microservice oriented architecture, one obstacle to overcome is identity authentication and authorization.
Most of the time, the propagation of the end user security context tends to stop at the API gateway layer, and a more generic security mechanism such as a service account, with basic authentication for example, steps in to secure traffic between microservices. In that case there is no way of telling if an end user is allowed to access a specific resource. This also introduces the risk of resources getting exposed due to service accounts which might have super-user privileges. So, a better solution would be to propagate the end user security context to every microservice that is being called during the lifetime of the request.
Securing each step
Typically, in such an architecture you would have one or multiple identity providers which can issue tokens to end users. Usually this is done using OpenID Connect, OAuth or JSON Web Tokens. Either way, these tokens are usually put in the
Authorization header of the request. When the request is received by the first microservice, it would check if this token is a valid token at the identity provider and otherwise throw a HTTP 401/403 if not.
When that microservice has to make a request to another microservice to fetch additional data to construct the resource, the token has to be propagated to that other microservice as well. Most of the time the token is fetched from the authorization header at controller level and passed on to the method making the call to the next microservice. However, this makes the code completely dependent on passing tokens which you most likely don't want. To overcome this problem I've made a
DelegatingHandler which can be used by any instance of
public class AuthorizationHeaderHandler : DelegatingHandler
private readonly IHttpContextAccessor _httpContextAccessor;
public AuthorizationHeaderHandler(IHttpContextAccessor httpContextAccessor)
_httpContextAccessor = httpContextAccessor;
protected override Task
SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
if (_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(HeaderNames.Authorization, out var authHeader))
request.Headers.Authorization = AuthenticationHeaderValue.Parse(authHeader);
return base.SendAsync(request, cancellationToken);
AuthorizationHeaderHandler will propagate the incoming token to request that is made using the
HttpClient that implements this handler. To get that token we have to access the current
HttpContext. Because we can't access
HttpContext in .NET Core outside a controller, there is a helper class called
HttpContextAccessor which we can use to access the current
AuthorizationHeaderHandler uses this helper class to get the authorization token from the current request headers and put it on the outgoing request just before it is send.
Now to add this delegating handler to any
HttpClient, using the new
HttpClientFactory that is available as of .NET Core 2, can be done in like so:
For this example i've used a named
AuthorizationHeaderHandler has to be added as
Transient dependency in order for it to be available to the
HttpClientFactory. Note that there is an extension method available to add the
When making a request to another microservice, for example:
public class Foo
private readonly IHttpClientFactory _httpClientFactory;
public Foo(IHttpClientFactory httpClientFactory)
_httpClientFactory = httpClientFactory;
public async Task<string> Bar()
var client = _httpClientFactory.CreateClient("AuthorizedClient");
return await client.GetStringAsync("http://somemicroserviceurl");
The authorization header is automatically set on every request that is made using the
HttpClient from the
HttpClientFactory. This way the end user security context can be propagated in a clean way instead of passing tokens everywhere and making your code dependent on it.
Check out the full source code at GitHub.