Role Based Authorization Using Jwt Token In Asp.Net Core Web Api



Published On Wednesday June 24, 2020
Reading Time: 3 minutes

My previous post described how we can authenticate a web API method using JWT. The post is the continuation of my previous post  JWT Authentication In Asp.Net Core Web API.

If you want to try out the codes on your own I have added a GitHub Repo with the complete code which you can find in the link https://github.com/rashik-tuladhar/JWT-Authentication-In-Asp.Net-Core

The tutorial will explain to you how we can use the value of the claim as a role for authorization.

Controller > AuthenticationController
public TokenDetails AuthenticateUser(TokenRequest tokenRequest) 
{ 
     var tokenDetails = new TokenDetails(); 
     if (tokenRequest.Username == "test" && tokenRequest.Password == "user") 
     { 
           tokenDetails.Email = "hello@rashik.com.np"; 
           tokenDetails.Name = "Rashik Tuladhar"; 
           tokenDetails.Roles = "auth.weather"; //roles are separated by comma(,) which is also mentioned in constant class named AuthorizationConstants 
     } 
     return tokenDetails; 
}

If you review the code section above you can find the tokenDetails.Roles which is used as a claim and embedded on the token.

public IActionResult CreateToken([FromBody] TokenRequest login) 
{ 
     IActionResult response = Unauthorized(); 
     var user = AuthenticateUser(login); 
     if (user.Name != null) 
     { 
          var claims = 
                new Claim[] { new Claim(ClaimTypes.Name, user.Name), 
                new Claim("Roles",user.Roles) }; 
                var tokenString = BuildToken(claims); 
          response = Ok(tokenString); 
     } 
     return response; 
}

The claim with the name Roles holds that role values assigned to the particular user in the form of comma-separated string.

Models

Model > AuthorizationConstants.cs
public class AuthorizationConstant
    {
        public const string Weather = "auth.weather";

        public const string NoRole = "auth.NoRole"; //Added For Authorization Check
    }

Add the above class on the Model folder which serves as the values of roles for authorization. You can see the similarity between the codes as:

//Models > AuthorizationConstants
public const string Weather = "auth.weather"; 

//Controller > Authentication Controller
tokenDetails.Roles = "auth.weather";

We just gave the name simply to distinguish specific roles and simply the codes.

We need to add a TypeFilterAttribute for checking the role authority.

Extensions > AuthorizationAttribute.cs
public class AuthorizationAttribute : TypeFilterAttribute
    {
        private readonly string _actionName = string.Empty;
        public AuthorizationAttribute(string claimValue) : base(typeof(ClaimRequirementFilter))
        {
            Arguments = new object[] { new Claim(_actionName, claimValue) };
        }

        public class ClaimRequirementFilter : IAsyncActionFilter
        {
            private readonly Claim _claim;

            public ClaimRequirementFilter(Claim claim)
            {
                _claim = claim;
            }

            public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
            {
                var permission = _claim.Value;
                var rolesString = context.HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Roles")?.ToString().Remove(0, 7);
                if (rolesString != null)
                {
                    var rolesList = rolesString.Split(',').ToArray();
                    bool hasPermission = rolesList.Contains(permission);
                    if (hasPermission)
                    {
                        await next();
                    }
                    else
                    {
                        if (context.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
                        {
                            await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(new { statuscode = HttpStatusCode.Forbidden, message = $"You are not authorized to use this function." }));
                            context.Result = new EmptyResult();
                        }
                        else
                        {
                            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                        }
                    }
                }
                else
                {
                    context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                }
            }
        }
    }

The type filter checks the availability of the roles to the user via the claims. For role authorization, we need to add a shortcode if we need to check the authority.

At first, let’s check for roles that are not listed in the claim value of auth.NoRole as we have only 2 roles on the claim as per the codes above i.e. auth.weather, auth.FullName.

Let’s make a change on the controller WeatherForecastController

Controller > WeatherForecastController
[HttpGet]
        [Authorization(AuthorizationConstant.NoRole)] //Added Code For Authorization
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }

we have added an Attribute [Authorization(AuthorizationConstant.NoRole)] now the attribute calls the AuthorizationAttribute type filter, the type filter check the availability of the role in the claim string with name Roles, and return either the user is authorized or not with status codes.

  1. Get the token as in the previous post.
  2. Add the authorization as Bearer and call the API weatherforecast.

You will simply get a 401 Unauthorized response as auth.NoRole was never assigned as a claim to the token. Now for checking the success lets just replace the Attribute Code [Authorization(AuthorizationConstant.NoRole)] with [Authorization(AuthorizationConstant.Weather)]. Follow the above 2 steps we could get the response and status code 200.

These simple steps help us achieve roles authorization via claims in JWT token.

Do share the post if you find the blog post effective and helpful.

#SharingIsCaring 🙂

0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x