JWT Authentication With Role Policy in NET Core 3.1

Hendi Suhardja
5 min readAug 24, 2020

--

What is JSON Web Token?

A JWT technically is a mechanism to verify the owner of some JSON data. It’s an encoded string, which is URL safe, that can contain an unlimited amount of data (unlike a cookie), and it’s cryptographically signed.

When a server receives a JWT, it can guarantee the data it contains can be trusted because it’s signed by the source. No middleman can modify a JWT once it’s sent.

It’s important to note that a JWT guarantees data ownership but not encryption; the JSON data you store into a JWT can be seen by anyone that intercepts the token, as it’s just serialized, not encrypted.

For this reason, it’s highly recommended to use HTTPS with JWTs (and HTTPS in general, by the way). A very common use of a JWT token, and the one you should probably only use JWT for, is as an API authentication mechanism.

If you want to know further, you could read https://jwt.io/introduction/

TIPS : You could decode your jwt.io at https://jwt.io/

Prerequisites

Implementing JWT on Web API

  • Open nuget package manager, install “Microsoft.AspNetCore.Authentication.JwtBearer”
  • Open appsetting.json, create new configuration for JWTSecret, paste JWT Secret that you get from above step
"AppConfiguration": {"JWTSecret": "C8914496D6DA3952881FA4108718EE9BBC01F0E922CBA96DD9D22C1067BEDC53"}
  • Create folder “Configurations” , then add class “AppConfiguration”, put below code
public class AppConfiguration{public string JWTSecret { get; set; }}
  • Open Startup.cs, on ConfigureService method, put below code.

“ValidIssuer” identifies the principal that issued the JWT. The processing of this claim is generally application specific. Put it with the your server’s domain that issue the JWT

“ValidAudience” identifies the recipients that the JWT is intended for. Put it with your API’s domain

In my case, JWT Token issuer and API domain is in 1 same project, that’s why I put with http://localhost:44307

services.Configure<AppConfiguration>(Configuration.GetSection("AppConfiguration"));var appConfig = Configuration.GetSection("AppConfiguration").Get<AppConfiguration>();services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{options.RequireHttpsMetadata = false;options.SaveToken = true;options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = "http://localhost:44307",ValidAudience = "http://localhost:44307",IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appConfig.JWTSecret)),ClockSkew = TimeSpan.Zero};});
  • On startup.cs, method Configure, add below code between UseRouting and UseAuthorization
app.UseAuthentication();
  • Add new folder “Policies” on project, add new class “JWTPolicies”, add below code
using Microsoft.AspNetCore.Authorization;namespace JWTAuth.Policies{public class JWTPolicies{public const string Admin = "Admin";public const string User = "User";public static AuthorizationPolicy AdminPolicy(){return new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole(Admin).Build();}public static AuthorizationPolicy UserPolicy(){return new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole(User).Build();}}}
  • On Startup.cs, put below code inside method ConfigureServices
services.AddAuthorization(config =>{config.AddPolicy(JWTPolicies.Admin, JWTPolicies.AdminPolicy());config.AddPolicy(JWTPolicies.User, JWTPolicies.UserPolicy());});
  • Create new folder “Models” then add new class “User”, put below code
namespace JWTAuth.Models{public class User{public string UserName { get; set; }public string FullName { get; set; }public string Password { get; set; }public string UserRole { get; set; }}}
  • On folder Controllers, add new Controller “LoginController”, put below code (We will put dummy data for user login, password will be “123456”)
using JWTAuth.Configurations;using JWTAuth.Models;using JWTAuth.Policies;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Identity;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Options;using Microsoft.IdentityModel.Tokens;using System;using System.Collections.Generic;using System.IdentityModel.Tokens.Jwt;using System.Linq;using System.Security.Claims;using System.Text;namespace JWTAuth.Controllers{[ApiController][Route("[controller]")]public class LoginController : ControllerBase{private List<User> appUsers = new List<User>{new User { FullName = "Test Admin", UserName = "admin", Password = "AQAAAAEAACcQAAAAEF5EK1dj6LrG1K8H4BN/A/sUZwvbA916/MtPeaDpEOo5wt6pfkv7NsPpL4jGfkXEtw==", UserRole = JWTPolicies.Admin },new User { FullName = "Test User", UserName = "user", Password = "AQAAAAEAACcQAAAAEKeF62t/IDHbeVihkPJqdXLdztwLH98acHFLKdqTzndQIsDC79o2y0j7/mkQeZRyog==", UserRole = JWTPolicies.User }};private readonly AppConfiguration _appConfiguration;public LoginController(IOptions<AppConfiguration> appConfiguration){_appConfiguration = appConfiguration.Value;}[HttpPost][AllowAnonymous]public IActionResult Login([FromBody]User login){IActionResult response = Unauthorized();User user = AuthenticateUser(login);if (user != null){var tokenString = GenerateJWTToken(user);response = Ok(new{token = tokenString,userDetails = user,});}return response;}User AuthenticateUser(User loginCredentials){User user = appUsers.SingleOrDefault(x => x.UserName == loginCredentials.UserName);if(user != null){PasswordHasher<User> passwordHasher = new PasswordHasher<User>();if (ValidatePassword(user, loginCredentials.Password, passwordHasher)){return user;}}return null;}private bool ValidatePassword(User user, string password, IPasswordHasher<User> passwordHasher){var passwordHash = passwordHasher.HashPassword(user, password);return passwordHasher.VerifyHashedPassword(user, user.Password, password) != PasswordVerificationResult.Failed;}string GenerateJWTToken(User userInfo){var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appConfiguration.JWTSecret));var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);var claims = new[]{new Claim(JwtRegisteredClaimNames.Sub, userInfo.UserName),new Claim("fullName", userInfo.FullName.ToString()),new Claim("role", userInfo.UserRole),new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),};var token = new JwtSecurityToken(issuer: "http://localhost:44307",audience: "http://localhost:44307",claims: claims,expires: DateTime.Now.AddMinutes(30),signingCredentials: credentials);return new JwtSecurityTokenHandler().WriteToken(token);}}}
  • Create controller for testing “UserController”, we will create 2 actions, 1 action for Admin and 1 action for User
using JWTAuth.Policies;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;namespace JWTAuth.Controllers{[ApiController][Route("[controller]")]public class UserController : ControllerBase{[HttpGet][Route("GetUserData")][Authorize(Policy = JWTPolicies.User)]public IActionResult GetUserData(){return Ok("This is a response from user method");}[HttpGet][Route("GetAdminData")][Authorize(Policy = JWTPolicies.Admin)]public IActionResult GetAdminData(){return Ok("This is a response from Admin method");}}}

Test Application

  • Open your POSTMAN, select POST method
  • Put “https://localhost:44307/Login” on URL
  • Put below on Body
{"UserName" : "admin","Password" : "123456"}

Copy token from the response, and paste on jwt.io, you will see your decoded data

  • Now let’s test if we could access API with Admin Policy, open Authorization Tab, choose Bearer Token, copy your token to field “Token”. On Url, paste “https://localhost:44307/User/GetAdminData”, then click “Send”. You will see the response, so it indicate that you could access the API
  • Now let’s test if we could not access API endpoint without Admin Policy. Fill on Url “https://localhost:44307/User/GetUserData”. click “Send”, it will return Forbidden (403) response

Thanks for reading

--

--

No responses yet