question

DsLond-1165 avatar image
0 Votes"
DsLond-1165 asked ZhiLv-MSFT answered

JWT with authorization client side

Hello,
I have created web api server with jwt auth. In jwt I can get role claims for users. Roles are user, manager and admin.

My client is asp.net core MVC. So I have a layout page with menu items. We can use "@if(User.IsInRole("Admin"))" to hide/show menu items in web app without consuming api.. My question is how can I make menu items show and hide based on user role from jwt consuming api and what is the best practice for this (action or auth filters, middle ware etc.).

dotnet-aspnet-core-webapi
· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

MVC applications are browser based application and do not use a JWT authorization they use an authentication cookie. A JWT is used when the client is code. See the docs for how to configure and create an authorization cookie.

Use cookie authentication without ASP.NET Core Identity


0 Votes 0 ·

I am saving jwt in session. Angular and react are also using Jwt as clients. I want to use MVC instead. Views and controllers and serialize JSON responses to models.

0 Votes 0 ·

Again, browser based applications like MVC use an authentication cookie to authorize requests. Storing a JWT in Session is fine but the approach has nothing to do with authorization in MVC unless you wrote custom code. I would simply implement cookie authentication as illustrated in the link I provided in my first response.

0 Votes 0 ·
Show more comments

1 Answer

ZhiLv-MSFT avatar image
1 Vote"
ZhiLv-MSFT answered

Hi @DsLond-1165,

In the MVC application, you can also configure the application using JWT authentication, with the same key, and set the Issuer and Audience. Then, you can create a custom middleware to get user claims from the JWT token, and then add them into the current httpcontext. After that in the MVC view page, you can access the claims from HttpContext.User.

Please refer the following sample code (in the following sample, I disable verifying Issuer and Audience):

ConfigureServices method: configure the MVC application use JWT authentication.

     public void ConfigureServices(IServiceCollection services)
     {
         ...
         services.AddAuthentication(option =>
         {
             option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
             option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

         }).AddJwtBearer(options =>
         {
             options.TokenValidationParameters = new TokenValidationParameters
             {
                 ValidateIssuer = false,
                 ValidateAudience = false,
                 ValidateLifetime = false,
                 ValidateIssuerSigningKey = true,
                 ValidIssuer = Configuration["Jwt:Issuer"],
                 ValidAudience = Configuration["Jwt:Audience"],
                 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) //Configuration["JwtToken:SecretKey"]
             };
         });
     }

In the appsettings.json file, add the same JWT configuration:

   "Jwt": {
     "Key": "ThisismySecretKey",
     "Issuer": "Test.com",
     "Audience": "Test.com"
   },

Create a JWTMiddleware middleware with the following code:

 public class JWTMiddleware
 {
     private readonly RequestDelegate _next;
     private readonly IConfiguration _configuration; 

     public JWTMiddleware(RequestDelegate next, IConfiguration configuration )
     {
         _next = next;
         _configuration = configuration; 
     }

     public async Task Invoke(HttpContext context)
     {
         var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

         if (token != null)
             attachAccountToContext(context, token);

         await _next(context);
     }

     private void attachAccountToContext(HttpContext context, string token)
     {
         try
         {
             var tokenHandler = new JwtSecurityTokenHandler();
             var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Key"]);
             tokenHandler.ValidateToken(token, new TokenValidationParameters
             {
                 ValidateIssuerSigningKey = true,
                 IssuerSigningKey = new SymmetricSecurityKey(key),
                 ValidateIssuer = false,
                 ValidateAudience = false,
                 // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
                 ClockSkew = TimeSpan.Zero
             }, out SecurityToken validatedToken);
                
             var jwtToken = (JwtSecurityToken)validatedToken;
                
             //get the user name and role from the JWT token.
             var username = jwtToken.Claims.First(x => x.Type == "username").Value;
             var role = jwtToken.Claims.First(x => x.Type == "role").Value;


             var userClaims = new List<Claim>()
             {
                 new Claim("UserName", username), 
                 new Claim("Role", role)
              };

             var userIdentity = new ClaimsIdentity(userClaims, "User Identity");

             var userPrincipal = new ClaimsPrincipal(new[] { userIdentity });
                 
             // attach account to context on successful jwt validation 
             //var user = new MVCWebApplication.Data.User();
             //user.UserName = "aa@hotmail.com";
             //context.Items["User"] = user;

             context.SignInAsync(userPrincipal);
         }
         catch (Exception ex)
         {
             // do nothing if jwt validation fails
             // account is not attached to context so request won't have access to secure routes
             throw new Exception(ex.Message);
         }
     }
 }

Register the JWTMiddleware in the Configure method:

         app.UseAuthentication();
         app.UseAuthorization();
         app.UseMiddleware<JWTMiddleware>();

In the MVC controller action method:

     [Authorize(AuthenticationSchemes = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)]
     public IActionResult Index()
     {
         return View(_context.Students.ToList());
     }

and in the View page:

 @{
     if (User.Claims.FirstOrDefault(c => c.Type.Contains("role"))?.Value == "Admin")
     { 
         <h2>@User.Claims.FirstOrDefault(c => c.Type.Contains("username")).Value</h2>
         <h2>@User.Claims.FirstOrDefault(c => c.Type.Contains("role")).Value</h2>
     }
 }

The result as below: When calling the action method with JWT token, if the user's role is "Admin", it will show the username and role name.

137343-image.png

Reference: Create And Validate JWT Token In .NET 5.0


If the answer is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

Best regards,
Dillion


image.png (69.6 KiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.