6

In an ASP.NET Core API project, I need to validate another JWT Bearer token located in header different than the Authorization header. For example, imagine sending a GET request to get products to /api/products with a Bearer token in a header named AccessToken.

curl --location --request GET 'https://localhost/api/products' \
--header 'AccessToken: <bearer_token>'

I'm referencing the Microsoft.AspNetCore.Authentication.JwtBearer package and setting authentication in the API project like this:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => Configuration.Bind("JwtSettings", options));

However, I cannot find anything regarding a header name inside the JwtBearerOptions Class.

How can I configure the JWT authentication to read the JWT from a header named "AccessToken"? Is it even possible using the Microsoft.AspNetCore.Authentication.JwtBearer package?

2
  • 1
    Per the source: github.com/dotnet/aspnetcore/blob/…, this middleware will ONLY look at the auth header. If you want to do something else, you'll need to write that middleware yourself. Commented Aug 27, 2021 at 20:56
  • 1
    You have to write a custom middleware inside your startup. Commented Aug 27, 2021 at 21:59

1 Answer 1

17

The solutions seems to be to use the JwtBearerEvents class. In it, there's a delegate property named OnMessageReceived that it's "invoked when a protocol message is first received". The delegate will pass a object of type of MessageReceivedContext, where it has a property named Token that according to the documentation "This will give the application an opportunity to retrieve a token from an alternative location".

Create a class that inherits from JwtBearerEvents and in the OnMessageReceived event set the token in the context object to the value from the header "AccessToken".

/// <summary>
/// Singleton class handler of events related to JWT authentication
/// </summary>
public class AuthEventsHandler : JwtBearerEvents
{
    private const string BearerPrefix = "Bearer ";

    private AuthEventsHandler() => OnMessageReceived = MessageReceivedHandler;

    /// <summary>
    /// Gets single available instance of <see cref="AuthEventsHandler"/>
    /// </summary>
    public static AuthEventsHandler Instance { get; } = new AuthEventsHandler();

    private Task MessageReceivedHandler(MessageReceivedContext context)
    {
        if (context.Request.Headers.TryGetValue("AccessToken", out StringValues headerValue))
        {
            string token = headerValue;
            if (!string.IsNullOrEmpty(token) && token.StartsWith(BearerPrefix))
            {
                token = token.Substring(BearerPrefix.Length);
            }

            context.Token = token;
        }

        return Task.CompletedTask;
    }
}

Finally, add the events class to the JWT authentication at the Startup class.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => 
{
    Configuration.Bind("JwtSettings", options);
    options.Events = AuthEventsHandler.Instance;
});
Sign up to request clarification or add additional context in comments.

5 Comments

This works, however if the token isn't found, the handler will fallback to checking the normal Authorization header. This means the token can be sent in either header which may not be wanted. To prevent that from happening, you need to call context.NoResult() if the token isn't found. The handler will return early if it sees the result has been set, rather than continuing normal processing.
mate you're a legend - there are no resources about on how to use the JwtBearerEvents at all - this helped me a ton. I post the token as a form content and this allows me to use that instead of the auth header.
@pinkfloydx33 you’re right, the code is missing a section to avoid falling back on the default of using the Authorize header. I completely missed it, good catch!
Wish I could give you massive kudos for this. Awesome find and explanation!
Very valuable answer. One thing I'd mention is use ignore case while checking if it starts with "Bearer": token.StartsWith(BearerPrefix, StringComparison.InvariantCultureIgnoreCase) My tokens would sometimes start with "Bearer ..." and the other times with "bearer ..."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.