Skimrande
Skimrande

Reputation: 831

Adding Authorization Header (Authorization: Bearer {access_token}) to specific route in Ocelot API Gateway

I have a web application making requests to different APIs through an Ocelot API gateway. One of the endpoints require authentication via JWT sent as an Authorization header in the format "Authorization: Bearer {access_token}" (the "Bearer" keyword identifying it as a bearer/token authentication scheme). This token is specific for the app and is not used to authenticate with the gateway (which uses a different authentication scheme), rather it is stored as a claim in the application's secure HttpOnly cookie.

While Ocelot does support transforming claims into headers (https://ocelot.readthedocs.io/en/latest/features/claimstransformation.html),

"AddHeadersToRequest": {
    "HeaderName": "Claims[ClaimName] > value[0] > |"
},

it only supports one value and no dynamic headers/string concatenation. In other words

"AddHeadersToRequest": {
    "Authorization": "Bearer Claims[ClaimName] > value[0] > |"
},

would not work (such a request obviously results in error 401). So my question is, how do I add a proper authorization header/bearer token to a specific Ocelot route?

Upvotes: 2

Views: 5532

Answers (1)

Skimrande
Skimrande

Reputation: 831

Delegating Handlers (https://learn.microsoft.com/en-us/dotnet/api/system.net.http.delegatinghandler?view=net-5.0, https://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html ), adding "stuff" to the request before it is sent, turned out to be very suitable for this task. As the Ocelot claims transformation takes place before they are called, it is simply a matter of transforming the claims to headers, then concatenating their values and adding them as a new header.

First edit ocelot.json:

"AddHeadersToRequest": {
    "AccessToken": "Claims[AccessToken] > value[0] > |"
},
"DelegatingHandlers": [
    "HeaderDelegatingHandler"
]

Then create a HeaderDelegatingHandler class inheriting from DelegatingHandler:

public class HeaderDelegatingHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HeaderDelegatingHandler (IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IEnumerable<string> headerValues;
        if (request.Headers.TryGetValues("AccessToken", out headerValues))
        {
            string accessToken = headerValues.First();

            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            request.Headers.Remove("AccessToken");
        }

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

Remember to add the Delegating Handler to ConfigureServices (can either be set as global or not):

services.AddOcelot()
        .AddDelegatingHandler<HeaderDelegatingHandler>();

Tom Pallister's (https://github.com/ThreeMammals/Ocelot/issues/468), rh101's (https://github.com/ThreeMammals/Ocelot/issues/1267#issuecomment-649299048) and PaulD's (https://stackoverflow.com/a/60609784/2768479) posts shed light on this issue.

Upvotes: 3

Related Questions