Search code examples
c#asp.net-corejwtasp.net-core-authenticationhandler

Read JWT token from different HTTP header in ASP.NET Core


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?


Solution

  • 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;
    });