We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept
Card image cap

Scenario: Validating JWT Token for Varying Validation Params in ASP.NET Core

ASP.NET Core JWT Authentication  • Posted 2 months ago

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 packages. Developers can enforce Authentication middleware onto incoming requests by adding the AddAuthentication() middleware to the service pipeline. Apart from JWT Bearer authentication middleware which focuses on validating Jwt token header in incoming http requests and OpenId middlewares which validate Users and sets up Session Cookies in web applications.

In addition, we are also provided an option of further customisation by means of implementing IAuthenticationHandler or extending AuthenticationHandler class which provides base for token validation.

Implementing Custom Authentication Scheme and Handler in ASP.NET Core

Consider an example where in 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 overriden 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 validatedToken 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));
            }
        }

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.

asp.net core custom authentication handler custom authentication handler asp.net core asp.net core custom authentication scheme custom authentication scheme asp.net core asp.net core custom authentication asp.net core 3 custom authentication asp.net core 3.1 custom authentication .net core custom authentication scheme .net core custom authentication handler asp net core custom authentication custom authentication middleware asp.net core .net core custom authentication .net core 3.1 custom authentication asp.net core 3 custom authentication middleware custom authenticationhandler custom authentication asp.net core custom authentication handler .net core custom authentication middleware asp.net core 2 custom authentication middleware asp.net core custom authentication middleware net core custom authentication aspnet core custom authentication custom authentication .net core asp.net custom authentication .net core 3 custom authentication