JWT Authentication & Authorization in .NET Core 3.1

Vaibhav Bhapkar
7 min readApr 10, 2020

--

In this article, we will see how we can implement JWT authentication and authorization in .NET core.

What is JWT?

JWT is an acronym for JSON Web Token. JWT is a way for securely transmitting information between parties as a JSON object. This information is verified and trusted because it’s digitally signed. JWT’s can be using a secret (with the HMAC algorithm) or public/private key pair using RSA/ECDSA.

JWT structure contains three parts,

Header

Payload

Signature

It looks something like this,

{“token: “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxOYW1lIjoiVmFpYmhhdiBCaGFwa2FyIiwicm9sZSI6IkFkbWluIiwianRpIjoiMTJjY2JmMWQtZDRhOS00ODUyLWE5YTgtMTRiY2Y3NzA0MmQ1IiwiZXhwIjoxNTg2NTAxNzkxLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDMzNi8iLCJhdWQiOiJodHRwczovL2xvY2FsaG9zdDo0NDMzNi8ifQ.XS3LBDSRMhcJmUi2itgBbPPhrdbX2cFgC7tZ7X_einM”,“userDetails”:{“userName: “admin”,“fullName: “Vaibhav Bhapkar”,“password: “1234”,“userRole: “Admin”}

Header.Payload.Signature

Let’s break these parts and dive deep into each of it,

Header:

The header consists of two-part one is signing algorithm (HMAC, SHA256, etc.) and the other one is the type of the token which is JWT.

Example:{“alg”: “HMAC”,“typ”: “JWT”}

This JSON form is Base64Url encoded to form the first part.

Payload:

The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. Again the payload formed JSON is Base64Url encoded to form second part.

Signature:

For the signature created you need to use encoded header, encoded payload, and secret with the algorithm specified at the header.

Example:HMAC(base64UrlEncode(header) + “.” +base64UrlEncode(payload),secret)

Putting all of these three parts together form Json Web Token.

JWT Authentication & Authorization in .NET Core 3.1

Requirements:

Visual studio or visual studio code installed with .Net Core 3.1

Steps to follow:

  1. Create a new project:

Visual Studio -> Create New Project -> Asp.Net Core Web application -> Select Web Api Template -> Create.

2. Install required a package of JWT from NuGet Package:

Right-click on project -> Manage NuGet package -> On browse section search for (Microsoft.AspNetCore.Authentication.JwtBearer) -> install.

3. Update Startup.cs ConfigureService() to register JWT authentication scheme,

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 = Configuration[“Jwt:Issuer”],ValidAudience = Configuration[“Jwt:Audience”],IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[“Jwt:SecretKey”])),ClockSkew = TimeSpan.Zero};});

Token validation parameters are described below,

ValidateIssuerSigningKey — Gets or sets a boolean that controls if validation of the SecurityKey that signed the securityToken is called.

ValidIssuer — Gets or sets a String that represents a valid issuer that will be used to check against the token’s issuer.

ValidateIssuer — Gets or sets a value indicating whether the Issuer should be validated. True means Yes validation required.

ValidAudience — Gets or sets a string that represents a valid audience that will be used to check against the token’s audience

ValidateAudience — Gets or sets a boolean to control if the audience will be validated during token validation.

ValidateLifetime — Gets or sets a boolean to control if the lifetime will be validated during token validation.

IssuerSigningKey is the public key used for validating incoming JWT tokens. By specifying a key here

4. A further step is to change in the appsettings.json with required details for JWT authentication scheme,

“Jwt”: {“SecretKey”: “VaibhavBhapkar”,“Issuer”: “https://localhost:44336/",“Audience”: “https://localhost:44336/"},

5. The next step is to make an authentication service available to the application to do this we need a call app.UseAuthentication in Configure() method,

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseHttpsRedirection();app.UseRouting();app.UseAuthentication();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}

6. Update Startup.cs file ConfigureService() method to add authorization code,

services.AddAuthorization(config =>{config.AddPolicy(Policies.Admin, Policies.AdminPolicy());config.AddPolicy(Policies.User, Policies.UserPolicy());});

For authorization we define policies class there we defined the policy defined according to roles as follows,

using Microsoft.AspNetCore.Authorization;using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace JWTAuthenticationExample.Models{public class Policies{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();}}}

7. Generate JSON Web Token:

We have to create a new API controller called LoginController and defined Login() method inside it, this method is responsible for the generation of JWT. we need to mark this method as AllowAnonymous attribute to bypass the authentication this method expects the User model object with a username and password parameter.

We have to define one more method called AuthenticateUser() which will check for user exists or not and return User object with other details if exists. This AuthenticateUser() method called inside Login() method once the user is authenticated we have to define one more method called GenerateJWTToken() where we have created a JWT using the JwtSecurityToken class. we need to create an object of this class bypassing some of the parameters to constructor includes issuer, audience, claims, expires, and signing credentials.

Claims here are the data contained by token they are informed about the user which is used to authorize the access the resource. Claims are formed using key-value pairs here in our case we are storing full name and role using claims.

Finally, JwtSecurityTokenHandler.WriteToken method is used to generate the JWT. This method expects an object of the JwtSecurityToken class.

User Model class:

using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace JWTAuthenticationExample.Models{public class User{public string UserName { get; set; }public string FullName { get; set; }public string Password { get; set; }public string UserRole { get; set; }}}

LoginController Class:

using System;using System.Collections.Generic;using System.IdentityModel.Tokens.Jwt;using System.Linq;using System.Security.Claims;using System.Text;using System.Threading.Tasks;using JWTAuthenticationExample.Models;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.Logging;using Microsoft.IdentityModel.Tokens;namespace JWTAuthenticationExample.Controllers{[ApiController][Route(“[controller]”)]public class LoginController : ControllerBase{private readonly IConfiguration _config;private List<User> appUsers = new List<User>{new User { FullName = “Vaibhav Bhapkar”, UserName = “admin”, Password = “1234”, UserRole = “Admin” },new User { FullName = “Test User”, UserName = “user”, Password = “1234”, UserRole = “User” }};public LoginController(IConfiguration config){_config = config;}[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 && x.Password == loginCredentials.Password);return user;}string GenerateJWTToken(User userInfo){var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config[“Jwt:SecretKey”]));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: _config[“Jwt:Issuer”],audience: _config[“Jwt:Audience”],claims: claims,expires: DateTime.Now.AddMinutes(30),signingCredentials: credentials);return new JwtSecurityTokenHandler().WriteToken(token);}}}

8. Defining the methods according to the role to check authorization:

We have to create UserController which contains the method according to Roles i.e User and Admin. If you want to restrict the method access we can do this with Authorize attribute.

- When we place the Authorize attribute on the controller itself, the authorize attribute applies to all of the actions inside.

- MVC framework will not allow a request to reach an action protected by this attribute unless the user passes an authorization check.

- By default, if you use no other parameters, the only check the Authorize attribute will make is a check to ensure the user is logged in so we know their identity.

- But you can use parameters to specify any fancy custom authorization policy that you like

In our case, we are using policies to restrict access to methods.

using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using JWTAuthenticationExample.Models;using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Logging;namespace JWTAuthenticationExample.Controllers{[ApiController][Route(“[controller]”)]public class UserController : ControllerBase{[HttpGet][Route(“GetUserData”)][Authorize(Policy = Policies.User)]public IActionResult GetUserData(){return Ok(“This is a response from user method”);}[HttpGet][Route(“GetAdminData”)][Authorize(Policy = Policies.Admin)]public IActionResult GetAdminData(){return Ok(“This is a response from Admin method”);}}}

Result:

Accessing login with wrong credentials,

After posting requests with wrong data you will end up by getting 403 unauthorized errors.

Accessing the login method with admin credentials,

After posting requests with the correct credential you will get a response with a generated token and a user object.

Accessing admin role accessible method with a generated token of admin,

With the generated token of admin, you are allowed to access the only method which has access for admin.

Accessing user role accessible method with a generated token of admin,

With admin generated token if you accessing the user-accessible method you will end up with 403 access forbidden response.

Please click here to check out the code base for this.

Reference: https://www.c-sharpcorner.com/article/policy-based-authorization-with-angular-and-asp-net-core-using-jwt/

Thank You, See you in the next article !!

You can reach out to me here,

LinkedIn: https://www.linkedin.com/in/vaibhav-bhapkar

Email: vaibhavbhapkar.medium@gmail.com

--

--

Vaibhav Bhapkar

Technical Speaker | Computer Engineer | Full Stack Web Developer | ML Enthusiast | * Knowledge Shared = Knowledge² *