How I tested my JWT token params in ASP.NET Core

Say you have to validate a JWT token based on the parameters supplied from an external service that varies for every incoming scheme.

Introduction

Authentication is a way of securing the application resources from unwanted access by validating incoming requests based on a defined mechanism.

In developing applications or APIs using ASP.NET Core, we are provided with rich set of libraries via the Microsoft.AspNetCore.Authentication package. Developers can enforce Authentication middleware onto incoming requests by adding the AddAuthentication() middleware to the service pipeline.

JWT Bearer authentication middleware focuses on validating Jwt token header in incoming http requests and OpenId middleware validates Users and sets up Session Cookies in web applications.

We are also provided an option of further customization by means of implementing IAuthenticationHandler or extending AuthenticationHandler class which provides base for token validation.

However…

Let’s say we are required to authenticate an incoming JWT token based on a varying set of token validation parameters passed onto the authentication handler via a service.

The scenario is like this: have an incoming authentication token which is of JWT format, which needs to be validated based on the validation parameters supplied from an external service. And this token parameter settings varies for every incoming scheme.

For this, we chose using a custom authentication handler because it provides better control over how the token validation happens for an incoming token. Our Authentication handler is as follows:

namespace ReadersApp.Middlewares.Authenticator
{
    public class AuthenticationSchemeConstants
    {
        public const string ReadersAuthenticationScheme = "ReadersAuth";
    }

    public class CustomAuthenticationOptions : AuthenticationSchemeOptions
    {
    }

    public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationOptions>
    {
         // stuff happens here
    }
}

All the validation logic happens in the abstract HandleAuthenticateAsync() method which is overridden from the AuthenticationHandler base class. In our scenario, the method is defined as below:

public class CustomAuthenticationHandler : AuthenticationHandler<CustomAuthenticationOptions>
    {
        private readonly IServiceProvider sp;
        private const string AuthorizationHeaderName = "Authorization";
        private const string applicationIdTag = "X-Application-Id";

        public CustomAuthenticationHandler(
            IOptionsMonitor<CustomAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IServiceProvider sp) : base(options, logger, encoder, clock)
        {
            this.sp = sp;
        }

        protected override Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey(AuthorizationHeaderName) || !Request.Headers.ContainsKey(applicationIdTag))
            {
                return Task.FromResult(AuthenticateResult.NoResult());
            }

            AuthenticationHeaderValue authenticationHeaderValue;
            
            if (!AuthenticationHeaderValue.TryParse(Request.Headers[AuthorizationHeaderName], out authenticationHeaderValue))
            {
                //Invalid Authorization header
                return Task.FromResult(AuthenticateResult.NoResult());
            }

            // token validation handling happens here
        }
    }

Observe that we have injected IServiceProvider into the constructor of the AuthenticationHandler, since we are to fetch the validation details from an external scoped service. Although the AuthenticationHandler has no scope restrictions and so the service can be injected directly into the constructor, this one is a bit secure which can avoid possible memory leaks.

Next, we would need to fetch values for validating token parameters from the service and then validate the incoming token for the parameters. For this we have a Get method which fetches the token validation parameters for a given scheme name from the scoped service. This is as below:

private TokenValidationParameters ConfigureJwtBearerOptions(string name)
    {
        TokenValidationParameters tvp = null;
        using (var scope = sp.CreateScope())
        {
            ISettingsService configuration =
                scope.ServiceProvider.GetRequiredService<ISettingsService>();
            tvp = configuration.GetTokenValidationParametersForScheme(name);
        }
        return tvp;
    }

This is called in the HandleAuthenticateAsync() method and used for validation.

protected override Task<AuthenticateResult> HandleAuthenticateAsync() { 
        AuthenticationHeaderValue authenticationHeaderValue;

        // Extract Authentication Token from the Headers
        if (!AuthenticationHeaderValue.TryParse(Request.Headers[AuthorizationHeaderName], 
                out authenticationHeaderValue))
        {
            //Invalid Authorization header
            return Task.FromResult(AuthenticateResult.NoResult());
        }

        // create a TokenHandler instance - for validation
        var tokenHandler = new JwtSecurityTokenHandler();

        string token = authenticationHeaderValue.Parameter;
        TokenValidationParameters params = ConfigureJwtBearerOptions(authenticationHeaderValue.Scheme);

        try
        {
            SecurityToken validatedToken;
            
            // validate incoming token for the given token parameters
            tokenHandler.ValidateToken(token, params, out validatedToken);
                
            // token is validated having reached this point    
        }
        catch (Exception ex)
        {
            return Task.FromResult(AuthenticateResult.Fail(ex));
        }
    }       

Now that the Token is validated, we would need to extract Claims from the validated token using the validated token generated from the ValidateToken() method as below:

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        try
        {
            // validate incoming token for the given token parameters
            // token is validated having reached this point
            
            // cast SecurityToken into JwtSecurityToken to access Claims
            var claims = (validatedToken as JwtSecurityToken).Claims;
            
            // create Identity using the Claims and Auth Scheme
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            
            // Generate an AuthTicket from the Claims and pass it to AuthResult
            var ticket = new AuthenticationTicket(principal, Scheme.Name);
                    
            return Task.FromResult(AuthenticateResult.Success(ticket));
                }
                catch (Exception ex)
                {
            return Task.FromResult(AuthenticateResult.Fail(ex));
        }
        catch(Exception ex) {
            return Task.FromResult(AuthenticateResult.Fail(ex));
        }
    }

When the above lines are executed, we have an Identity setup from the token Claims and this Identity is added onto create an AuthenticationTicket. This ticket is passed onto the AuthenticateResult.Success() method which sets up the Request.User.Identity object and adds up to the claims in the request.

This way, we can fetch and validate an incoming JWT token using a varying token parameters by means of a custom authentication handler.


Udemy APAC

Buy Me A Coffee

Found this article helpful? Please consider supporting!

Ram
Ram

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity. You can connect with me on Medium, Twitter or LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *