Authentication And Authorization Using JSON Web Tokens In .NET CORE 3.1

Introduction 

 
Security is one of the most important features of the web application. In most of the web applications, security is implemented through token-based authentication. In this article, We will discuss how to implement JWT authentication and authorization in .NET core 3.1.
 
Before exploring, let's discuss the common terms used in implementing JWT.
 

What is JWT?

 
JWT refers to JSON Web Token. JWT is used for securely transmitting information(data) between parties as a JSON object. It uses a digital signature to verify the information passed between the parties is verified or not.
 

Roles and Responsibilities of JSON Web Tokens

 
Authentication
 
Authentication is the process that verifies the user's credentials. Once the user's credentials are verified, users are allowed to access all the application features.
 
Authorization
 
Authorization is the process that allows the user's to access the application services, features, and routes. In order to authorize the application, it needs to be authenticated by any security technology.
 
JSON Web Tokens are used to authenticate and authorize the user. It is an open standard and compact mechanism for transmitting data across the server. It plays an important role in securely transmitting the data using the JSON object.
 
Let's begin from scratch...
 
Database and stored procedure
 
Create a table that contains a username and password as a login table and the stored procedure as "sp_Login" that returns the count. The count value returns 1 for the existing user in the table and the token is generated for the particular user who logged in.  
 
Create a New Project
 
Create a new project in Visual studio 2019 --> choose ASP.NET Web Application --> Choose API as a template and name it accordingly using proper naming conventions. 
 
Navigate to appsettings.json and add the following code.
 
A key ensures that the senders are authorized senders. Add the value of key in appsettings.json and ensure that the value of the key is quite hard. The secret key given in this appsettings.json acts as a symmetric key that is known by both the sender and the receiver. It is negotiated and distributed out of the band. 
  1. "Appsettings": {    
  2.     "key""This is my default token string"    
  3.   }    
Create a class to access the value of the key.
 
A loosely coupled class needs to be created to access the key given in appsettings.json. The name should be the same as given in the appsettings.json, the only use of this class to use the value of key in other classes by means of Injection dependencies. 
  1. using System;    
  2. using System.Collections.Generic;    
  3. using System.Linq;    
  4. using System.Threading.Tasks;    
  5.     
  6. namespace AuthToken.Models    
  7. {    
  8.     public class AppSettings    
  9.     {    
  10.         public string key { getset; }    
  11.     }    
  12. }    
Modify the ConfigureServices() method of the Startup.cs.
 
We need to register a JWT authentication schema by using the "AddAuthentication" method and specifying JwtBearerDefaults.AuthenticationScheme. In the code given below, we configure the authentication schema with JWT bearer options.
 
In this demo, we have specified the parameters must be taken into account to consider JWT as valid. TokenValidation Parameter class contains many properties to validate the JWT. Depending on the use case, we can add the other parameters too. 
  1. public void ConfigureServices(IServiceCollection services)    
  2.       {     
  3.                 
  4.           services.AddControllers();    
  5.     
  6.           //Read the Key from Appsettings    
  7.           var appsettingsRead = Configuration.GetSection("AppSettings");    
  8.     
  9.           //add the key to Services    
  10.          services.Configure<AppSettings>(appsettingsRead);    
  11.           //JWT Authentication    
  12.           var settings = appsettingsRead.Get<AppSettings>();    
  13.           var key = Encoding.ASCII.GetBytes(settings.key);    
  14.           services.AddAuthentication(au =>    
  15.           {    
  16.               au.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;    
  17.               au.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;    
  18.           }).AddJwtBearer(jwt =>    
  19.           {    
  20.               jwt.RequireHttpsMetadata = false;    
  21.               jwt.SaveToken = true;    
  22.               jwt.TokenValidationParameters = new TokenValidationParameters    
  23.               {    
  24.                   ValidateIssuerSigningKey = true,    
  25.                   IssuerSigningKey = new SymmetricSecurityKey(key),    
  26.                   ValidateIssuer = false,    
  27.                   ValidateAudience = false    
  28.               };    
  29.           });    
Here, I explained the parameter which is used in the above code. For more, refer to this link.
 
RequireHttpsMetadata
 
Used to check whether the HTTPS is required for the metadata address or authority. In development environments it should be disabled. By default, it is true.
 
SaveToken
 
It defines whether the token generated is stored in the AuthenticationProperties after a successful authorization.
 
TokenValidation Parameter
 
It is used to get or set the parameters used to validate the tokens are valid or not.
 
IssuerSigningKey
 
It is used to get or set the security key that is used for signature validation.
 
ValidateIssuerSigningKey
 
It is used to get or set the boolean that controls if validation of the security key that signed the securitytoken is called.
 
ValidateAudience
 
It is used to get or set a string that represents a valid audience that is used to check against the token's audience.
 
ValidateIssuer
 
It is used to get or set a string that represents a valid user which is used to check against the token's user.
 

Create a user class 

 
Create a user class as Userclass.cs for handling user input. In this example, I am using a username, password, and token. 
  1. using System;    
  2. using System.Collections.Generic;    
  3. using System.Linq;    
  4. using System.Threading.Tasks;    
  5.     
  6. namespace AuthToken.Models    
  7. {    
  8.     public class Userclass    
  9.     {    
  10.         public string username { getset; }    
  11.         public string password { getset; }    
  12.         public string Token { getset; }    
  13.     }    
  14. }    

Authentication service

 
Create an authentication service class and authentication service Interface as Login.cs and ILogin.cs. In the code given below, the user credentials are checked against the input parameters. For the valid users, token's will get generated.
 
In the below code, Injection dependency is used to get the value key from Appsettings.cs. SecurityTokenDescriptor class contains information that is used to create a security token. Claims are used to store information about the user like name, phone number, and role. In the code given below, a token is created based on the descriptions given in the token descriptor.
  1. using Microsoft.AspNetCore.Mvc;    
  2. using Microsoft.Extensions.Options;    
  3. using System;    
  4. using System.Collections.Generic;    
  5. using System.Data;    
  6. using System.Data.SqlClient;    
  7. using System.Linq;    
  8. using System.Security.Claims;    
  9. using System.Threading.Tasks;    
  10.     
  11. namespace AuthToken.Models    
  12. {    
  13.     public class Login_Token : Ilogin    
  14.     {    
  15.         string con = utility.Connection.Conn;    
  16.         public readonly AppSettings _appsettings;    
  17.     
  18.           
  19.         public Login_Token(IOptions<AppSettings> appsettings)    
  20.         {    
  21.             _appsettings = appsettings.Value;    
  22.     
  23.         }    
  24.     
  25.         public  bool post( Userclass user)    
  26.         {    
  27.             using (SqlConnection conn = new SqlConnection(con))    
  28.     
  29.             {    
  30.                 {    
  31.                     SqlCommand cmd = new SqlCommand("login_pro", conn);    
  32.                     cmd.CommandType = CommandType.StoredProcedure;    
  33.                     conn.Open();    
  34.     
  35.                     cmd.Parameters.AddWithValue("@Username", user.username);    
  36.                     cmd.Parameters.AddWithValue("@Password", user.password);    
  37.                     object read = cmd.ExecuteScalar();    
  38.                     int Variable = Convert.ToInt32(read);    
  39.     
  40.                     conn.Close();    
  41.     
  42.                     if (Variable == 1)    
  43.                     {    
  44.                         var tokenhandler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();    
  45.                         var keys = System.Text.Encoding.ASCII.GetBytes(_appsettings.key);    
  46.                         var tokendescriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor    
  47.                         {    
  48.                             Subject = new System.Security.Claims.ClaimsIdentity(new Claim[]    
  49.                         {    
  50.                 new Claim(ClaimTypes.Name,user.username.ToString()),    
  51.                 new Claim(ClaimTypes.Role,"Admin"),    
  52.                 new Claim(ClaimTypes.Version,"v2.1")    
  53.                         }),    
  54.                             Expires = DateTime.UtcNow.AddHours(3),    
  55.                             SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keys), Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature)    
  56.     
  57.                         };    
  58.                         var tokens = tokenhandler.CreateToken(tokendescriptor);    
  59.     
  60.                         user.Token = tokenhandler.WriteToken(tokens);    
  61.                         user.password = null;    
  62.                         return Variable == 1;    
  63.                             
  64.                     }    
  65.                     return false;    
  66.     
  67.                 }    
  68.             }    
  69.         }    
  70.     }    
  71. }    

Database Class

 
Create a database connection, either using db context or inline memory or using a connection string. In this project, a separate class utility.cs is created to hold the connection string values. 
  1. using System;    
  2. using System.Collections.Generic;    
  3. using System.Linq;    
  4. using System.Threading.Tasks;    
  5.     
  6. namespace AuthToken.utility    
  7. {    
  8.     public class Connection    
  9.     {    
  10.         public static string  Conn = "Server=********;Database=******;Trusted_Connection=True";    
  11.         public static string con { get => Conn; }    
  12.     }    
  13. }    
AuthenticationController.cs
 
Create an AuthenticationController.cs, as given below in the code.
 
In the code given below, for the valid user the post action method returns the user details along with the token. For an invalid user, HTTP status code return Not Found. 
  1. namespace AuthToken.Controllers    
  2. {    
  3.     [Route("[controller]")]    
  4.     [ApiController]    
  5.     public class AuthenticationController : ControllerBase    
  6.     
  7.     {    
  8.         public Ilogin _authservice;    
  9.     
  10.         public AuthenticationController( Ilogin authservice)    
  11.         {    
  12.             _authservice = authservice;    
  13.         }    
  14.     
  15.         [HttpPost("Login")]    
  16.         public IActionResult Token(Userclass user)    
  17.         {    
  18.                  
  19.             if (_authservice.post(user))    
  20.                  return Ok(user);    
  21.                        
  22.                          return BadRequest(new { message = "Not valid UserName" });    
  23.                             
  24.             }    
  25.     
  26.  } }    

Test the created API using Postman

 
In the output given below, the token is generated for the registered user in the database. Try using unregistered user, check the status code. In this project for the registered user, the token is generated. 
 
Authentication And Authorization Using JSON Web Tokens In .NET CORE 3.1
 

Token Concepts

 
Usually a token has a header, payload, and signature. The header contains JWT(type of and signing algorithm.) The payload contains claims, claims are the details about users or entities. Signature is used for verification.
 
In the signature text box give the value of the key and validate the signature is valid or not.
 
Authentication And Authorization Using JSON Web Tokens In .NET CORE 3.1
 

Authorization

 
Once authentication is done, we can authorize the other methods or controller by using the following steps given below. In this example, weatherforecast controller is authorized once the user is authenticated.
  
Note: Register authorization service in startup.cs class.
  1. [Authorize(AuthenticationSchemes = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme)]    
  2.    [ApiController]    
  3.       
  4.    [Route("[controller]")]    
  5.    public class WeatherForecastController : ControllerBase    
  6.    {    
  7.        private static readonly string[] Summaries = new[]    
  8.        {    
  9.            "Freezing""Bracing""Chilly""Cool""Mild""Warm""Balmy""Hot""Sweltering""Scorching"    
  10.        };    
WeatherForecast controller is authorized and the output obtained as given below.
 
Authentication And Authorization Using JSON Web Tokens In .NET CORE 3.1
 

Summary

 
In the upcoming article, we will discuss more detailed concepts in .NET core 3.1. Thanks for reading my article.


Similar Articles