Introduction
In this article, we will implement the JWT Token based Authentication using asp.net Core 6 by following the 3-tier architecture. If you want to learn 3-tier architecture, then click the below link.
JSON Web Token is an open standard that allows transmitting the data between parties as JSON is digitally signed, so the information is trusted and verified. JWT Token can be signed using secret (with HMAC) Algorithm or with the public or private key pairs using RSA Or ECDSA. JWT Token Authentication is very popular in Website Development.
Implementation of JSON Web Token in Asp.Net Core 6 Web API
First, Create the project of Asp.net Core Web API using the API Template given in Visual Studio or Visual Studio Code using CLI
- Dotnet new WebAPI -n JwtTokenAuthentication
Create the Project For Presentation Layer
Paste this command in the CLI and hit enter, CLI will create the Web API Template
Select the Name of your project
Select .Net 6
And then click the Create button, Visual Studio will create the template for us
Our Web API Template has been created. Install the following packages in the web API Template
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Microsoft.IdentityModel.JsonWebTokens
- System.IdentityModel.Tokens.Jwt
And we have discussed above that in this project we will use the three-tier architecture so our second step will be to create the layer in this project. Now we will add the Data Access layer to the project. For this, we will use the asp.net core library project.
Create the project for Data Access Layer
Click on your solution folder and then click on Add Button. I have already created all the projects but for the article, I will repeat all the steps.
Click on Add -> New Project
Select the Class Library Project and click on Next
After Clicking Next Select the Name of your project. As we know that this project is for Data access layer so our Convention of this project will be Name of Parent Project _ DAL. After giving the name hit the create button, visual studios will create the Class Library project using the support of Asp.net core 6.
Click Next and select the .net 6 Support
Then hit the Create Button and Visual Studio Will create the Class Library Project for using and now you can see that our Class Library project has been created.
Now we will create the Business access layer project. For this repeat the above process that we have learned for creating the data access layer.
Create the project for Business Access Layer
Write Click on your Solution and then click on Add
Add->New Project->Select the Class Library Project -> Name of the Project -> Next-> Select the Net Core 6 -> Create
After following the steps, we have now our business access layer project
Add the Reference of Projects
In this step, we need to add project references in the respective projects
Add the References of DAL and BAL in Presentation Layer
Select your API Project and right click on the project and click the Add button and click on project Reference for Presentation Layer. We need references of both data access layer and business access layer
Select both project and click Ok
Now you can see that the project references have been added to our project
Add the Data Access Layer References in Business access layer
Right Click on the Business Access layer project and click on Add and select the project references and in this layer we need only data access layer reference.
Select the DAL Project and Click on Ok Button
Now you can see that our Data Access layer (DAL) project references have been added to our business access layer (BAL) project.
Implementation of JSON Web Token in Our Code
For implementation, we will first complete the Data Access layer and then Business access layer and finally we will complete Presentation Layer
Data Access Layer
First, install the following Nuget Packages in the project.
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
In Data Access layer Create the flowing Folders
- Contracts
- Data
- Model
- Repository
Migration folder will be automatically added when we do migration
Contracts
In contracts, we define the interface for the project. In this project we will create the Generic Interface like Code is given below
Create the IRepository Generic Interface Class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JWTTokenAuthInAspNet6_DAL.Contracts {
public interface IRepository < T > {
public Task < T > Create(T _object);
public void Delete(T _object);
public void Update(T _object);
public IEnumerable < T > GetAll();
public T GetById(int Id);
}
}
Models
Create the Model class for AppUser and extends this class from Identity User Class IdentityUser
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JWTTokenAuthInAspNet6_DAL.Models {
public class AppUser: IdentityUser {
public int Id {
get;
set;
}
public string ? Name {
get;
set;
}
public string ? AccountType {
get;
set;
}
public string ? PhoneNo {
get;
set;
}
public string ? Password {
get;
set;
}
public string ? ShopName {
get;
set;
}
public string ? BusinessType {
get;
set;
}
public string ? UserRole {
get;
set;
}
public bool ? IsDeleted {
get;
set;
}
}
}
Data
In Data folder we create the application db.context class which is a very important class for creating Migration and Communication with database. We create the dbcontext class and extend that class from IdentityDbContext and pass our app user model class.
using JWTTokenAuthInAspNet6_DAL.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JWTTokenAuthInAspNet6_DAL.Data {
public class AppDbContext: IdentityDbContext < AppUser > {
public AppDbContext(DbContextOptions < AppDbContext > options): base(options) {}
protected override void OnModelCreating(ModelBuilder builder) {
base.OnModelCreating(builder);
}
public DbSet < AppUser > AppUsers {
get;
set;
}
}
}
Add the Db Set property in the application db Context Class for our app user Class
public DbSet<AppUser> AppUsers { get; set; }
Migrations
Now we will Create the Migration
Open the project Manager Console and select the Data Access Layer Project
Write the Following Commands
- add-migration JSONWebTokenM1
Then Update the database
Now we have the Migration Folder in our project
We have created the Migrations and updated the database
Repository
In the Repository we will write all the database access Methods for Create, Delete and Update and View the data
Create the Repository Class and Extends the IRepository Interface and implements the interface. Here we need the references of Db Context Class. Using that class we will implement all the CRUD methods in the Repository
Remember that our interface is generic and when we extends the
using JWTTokenAuthInAspNet6_DAL.Contracts;
using JWTTokenAuthInAspNet6_DAL.Data;
using JWTTokenAuthInAspNet6_DAL.Models;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JWTTokenAuthInAspNet6_DAL.Repositories {
public class RepositoryAppUser: IRepository < AppUser > {
private readonly AppDbContext _appDbContext;
private readonly ILogger _logger;
public RepositoryAppUser(ILogger < AppUser > logger) {
_logger = logger;
}
public async Task < AppUser > Create(AppUser appuser) {
try {
if (appuser != null) {
var obj = _appDbContext.Add < AppUser > (appuser);
await _appDbContext.SaveChangesAsync();
return obj.Entity;
} else {
return null;
}
} catch (Exception) {
throw;
}
}
public void Delete(AppUser appuser) {
try {
if (appuser != null) {
var obj = _appDbContext.Remove(appuser);
if (obj != null) {
_appDbContext.SaveChangesAsync();
}
}
} catch (Exception) {
throw;
}
}
public IEnumerable < AppUser > GetAll() {
try {
var obj = _appDbContext.AppUsers.ToList();
if (obj != null) return obj;
else return null;
} catch (Exception) {
throw;
}
}
public AppUser GetById(int Id) {
try {
if (Id != null) {
var Obj = _appDbContext.AppUsers.FirstOrDefault(x => x.Id == Id);
if (Obj != null) return Obj;
else return null;
} else {
return null;
}
} catch (Exception) {
throw;
}
}
public void Update(AppUser appuser) {
try {
if (appuser != null) {
var obj = _appDbContext.Update(appuser);
if (obj != null) _appDbContext.SaveChanges();
}
} catch (Exception) {
throw;
}
}
}
}
Business Access layer
In the business access layer, we write the services of our project in this project our structure will be like.
Here we have the services that implement the CRUD Logic of all the Repositories. In the services folder we create the AppUserServices Service class and here we will use the Repository for implementing the CRUD operations.
using JWTTokenAuthInAspNet6_DAL.Contracts;
using JWTTokenAuthInAspNet6_DAL.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JWTTokenAuthInAspNet6_BAL.Services {
public class ServiceAppUser {
public readonly IRepository < AppUser > _repository;
public ServiceAppUser(IRepository < AppUser > repository) {
_repository = repository;
}
//Create Method
public async Task < AppUser > AddUser(AppUser appUser) {
try {
if (appUser == null) {
throw new ArgumentNullException(nameof(appUser));
} else {
return await _repository.Create(appUser);
}
} catch (Exception) {
throw;
}
}
public void DeleteUser(int Id) {
try {
if (Id != 0) {
var obj = _repository.GetAll().Where(x => x.Id == Id).FirstOrDefault();
_repository.Delete(obj);
}
} catch (Exception) {
throw;
}
}
public void UpdateUser(int Id) {
try {
if (Id != 0) {
var obj = _repository.GetAll().Where(x => x.Id == Id).FirstOrDefault();
if (obj != null) _repository.Update(obj);
}
} catch (Exception) {
throw;
}
}
public IEnumerable < AppUser > GetAllUser() {
try {
return _repository.GetAll().ToList();
} catch (Exception) {
throw;
}
}
}
}
Presentation Layer
In our presentation layer our project structure will be like
In the Presentation layer we need the Following Packages for Implementation of JWT Auth and Entity Framework Operations
Microsoft.AspNetCore.Authentication.JwtBearer
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.IdentityModel.JsonWebTokens
System.IdentityModel.Tokens.Jwt
Common Folder
In Common folder we create the classes that are commonly used in our application for this project. We create the API Response class that contains only two properties Status and Message.
namespace JWTTokenAuthInAspNet6.Commons
{
public class Response
{
public string? Status { get; set; }
public string? Message { get; set; }
}
}
View Model
In View Model we have two View Model Classes like
- Register View Model
- Sign In View Model
Sign In View Model
namespace JWTTokenAuthInAspNet6.ViewModels {
public class LoginVM {
//public string? Id { get; set;}
public string ? UserName {
get;
set;
}
public string ? Password {
get;
set;
}
}
}
Register View Model
using Microsoft.AspNetCore.Identity;
namespace JWTTokenAuthInAspNet6.ViewModels {
public class RegisterVM {
public string ? Name {
get;
set;
}
public string ? AccountType {
get;
set;
}
public string ? PhoneNo {
get;
set;
}
public string ? Email {
get;
set;
}
public string ? Password {
get;
set;
}
public string ? ShopName {
get;
set;
}
public string ? BusinessType {
get;
set;
}
public string ? UserRole {
get;
set;
}
public bool ? IsDeleted {
get;
set;
}
}
}
AppSetting.Json Properties
In App setting Json File we are going to set the JSON Web Tokens Keys like
"JsonWebTokenKeys": {
"ValidateIssuerSigningKey": true,
"IssuerSigningKey": "64A63153-11C1-4919-9133-EFAF99A9B456",
"ValidateIssuer": true,
"ValidIssuer": "https://localhost:7212",
"ValidateAudience": true,
"ValidAudience": "https://localhost:7212",
"RequireExpirationTime": true,
"ValidateLifetime": true
},
Program.cs Class
Configure the Identity and JWT Token in Program.cs Class
using JWTTokenAuthInAspNet6_BAL.Services;
using JWTTokenAuthInAspNet6_DAL.Contracts;
using JWTTokenAuthInAspNet6_DAL.Data;
using JWTTokenAuthInAspNet6_DAL.Models;
using JWTTokenAuthInAspNet6_DAL.Repositories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
//Sql Dependency Injection
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext < AppDbContext > (options => options.UseSqlServer(connectionString));
//Add Service Injection
builder.Services.AddSingleton < IRepository < AppUser > , RepositoryAppUser > ();
builder.Services.AddSingleton < ServiceAppUser, ServiceAppUser > ();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "JWTToken_Auth_API", Version = "v1"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() {
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 1safsfsdfdfd\"",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
//Add Identity
builder.Services.AddIdentity < AppUser, IdentityRole > ().AddEntityFrameworkStores < AppDbContext > ();
//Add Jwt Token Functionality
// Add Authencation
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters() {
ValidateIssuerSigningKey = bool.Parse(builder.Configuration["JsonWebTokenKeys:ValidateIssuerSigningKey"]),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JsonWebTokenKeys:IssuerSigningKey"])),
ValidateIssuer = bool.Parse(builder.Configuration["JsonWebTokenKeys:ValidateIssuer"]),
ValidAudience = builder.Configuration["JsonWebTokenKeys:ValidAudience"],
ValidIssuer = builder.Configuration["JsonWebTokenKeys:ValidIssuer"],
ValidateAudience = bool.Parse(builder.Configuration["JsonWebTokenKeys:ValidateAudience"]),
RequireExpirationTime = bool.Parse(builder.Configuration["JsonWebTokenKeys:RequireExpirationTime"]),
ValidateLifetime = bool.Parse(builder.Configuration["JsonWebTokenKeys:ValidateLifetime"])
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsProduction()) {
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Controller
In the Controllers, we will write the Sign In and Register API End Point that is used to generate the Token when the user want to sign In to the Application.
Register API End Point
[HttpPost]
[Route("Register")]
public async Task < IActionResult > Register([FromBody] RegisterVM registerVM) {
var IsExist = await userManager.FindByNameAsync(registerVM.Name);
if (IsExist != null) return StatusCode(StatusCodes.Status500InternalServerError, new Response {
Status = "Error", Message = "User already exists!"
});
AppUser appUser = new AppUser {
UserName = registerVM.Name,
AccountType = registerVM.AccountType,
Email = registerVM.Email,
PhoneNumber = registerVM.PhoneNo,
Password = registerVM.Password,
ShopName = registerVM.ShopName,
BusinessType = registerVM.BusinessType,
UserRole = registerVM.UserRole,
IsDeleted = registerVM.IsDeleted
};
var result = await userManager.CreateAsync(appUser, registerVM.Password);
if (!result.Succeeded) return StatusCode(StatusCodes.Status500InternalServerError, new Response {
Status = "Error", Message = "User creation failed! Please check user details and try again."
});
if (!await roleManager.RoleExistsAsync(registerVM.UserRole)) await roleManager.CreateAsync(new IdentityRole(registerVM.UserRole));
if (await roleManager.RoleExistsAsync(registerVM.UserRole)) {
await userManager.AddToRoleAsync(appUser, registerVM.UserRole);
}
return Ok(new Response {
Status = "Success", Message = "User created successfully!"
});
}
Sign In API End Point
This API Endpoint will be used to generate the JWT Token when the user wants to sign in to the Application.
[HttpPost]
[Route("Login")]
public async Task < IActionResult > Login([FromBody] LoginVM loginVM) {
//var user1 = await userManager.FindByIdAsync(loginVM.Id);
var user = await userManager.FindByNameAsync(loginVM.UserName);
if (user != null && await userManager.CheckPasswordAsync(user, loginVM.Password)) {
var userRoles = await userManager.GetRolesAsync(user);
var authClaims = new List < Claim > {
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.MobilePhone, user.PhoneNumber),
new Claim(ClaimTypes.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
foreach(var userRole in userRoles) {
authClaims.Add(new Claim(ClaimTypes.Role, userRole));
}
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JsonWebTokenKeys:IssuerSigningKey"]));
var token = new JwtSecurityToken(expires: DateTime.Now.AddHours(3), claims: authClaims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256));
return Ok(new {
api_key = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo,
user = user,
Role = userRoles,
status = "User Login Successfully"
});
}
return Unauthorized();
}
Overall Controller Code
using JWTTokenAuthInAspNet6.Commons;
using JWTTokenAuthInAspNet6.ViewModels;
using JWTTokenAuthInAspNet6_DAL.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace JWTTokenAuthInAspNet6.Controllers {
[Route("api/[controller]")]
[ApiController]
public class AuthController: ControllerBase {
private readonly UserManager < AppUser > userManager;
private readonly RoleManager < IdentityRole > roleManager;
private readonly IConfiguration _configuration;
private readonly SignInManager < AppUser > signInManager;
public AuthController(UserManager < AppUser > userManager, SignInManager < AppUser > signInManager, RoleManager < IdentityRole > roleManager, IConfiguration configuration) {
this.userManager = userManager;
this.roleManager = roleManager;
_configuration = configuration;
this.signInManager = signInManager;
}
[HttpPost]
[Route("Register")]
public async Task < IActionResult > Register([FromBody] RegisterVM registerVM) {
var IsExist = await userManager.FindByNameAsync(registerVM.Name);
if (IsExist != null) return StatusCode(StatusCodes.Status500InternalServerError, new Response {
Status = "Error", Message = "User already exists!"
});
AppUser appUser = new AppUser {
UserName = registerVM.Name,
AccountType = registerVM.AccountType,
Email = registerVM.Email,
PhoneNumber = registerVM.PhoneNo,
Password = registerVM.Password,
ShopName = registerVM.ShopName,
BusinessType = registerVM.BusinessType,
UserRole = registerVM.UserRole,
IsDeleted = registerVM.IsDeleted
};
var result = await userManager.CreateAsync(appUser, registerVM.Password);
if (!result.Succeeded) return StatusCode(StatusCodes.Status500InternalServerError, new Response {
Status = "Error", Message = "User creation failed! Please check user details and try again."
});
if (!await roleManager.RoleExistsAsync(registerVM.UserRole)) await roleManager.CreateAsync(new IdentityRole(registerVM.UserRole));
if (await roleManager.RoleExistsAsync(registerVM.UserRole)) {
await userManager.AddToRoleAsync(appUser, registerVM.UserRole);
}
return Ok(new Response {
Status = "Success", Message = "User created successfully!"
});
}
[HttpPost]
[Route("Login")]
public async Task < IActionResult > Login([FromBody] LoginVM loginVM) {
//var user1 = await userManager.FindByIdAsync(loginVM.Id);
var user = await userManager.FindByNameAsync(loginVM.UserName);
if (user != null && await userManager.CheckPasswordAsync(user, loginVM.Password)) {
var userRoles = await userManager.GetRolesAsync(user);
var authClaims = new List < Claim > {
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.MobilePhone, user.PhoneNumber),
new Claim(ClaimTypes.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
foreach(var userRole in userRoles) {
authClaims.Add(new Claim(ClaimTypes.Role, userRole));
}
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JsonWebTokenKeys:IssuerSigningKey"]));
var token = new JwtSecurityToken(expires: DateTime.Now.AddHours(3), claims: authClaims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256));
return Ok(new {
api_key = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo,
user = user,
Role = userRoles,
status = "User Login Successfully"
});
}
return Unauthorized();
}
}
}
Output
Register the User
Sign In user using Created Credentials
Here we have the complete object of the user and JWT Token
Conclusion
JSON Web Token is an open standard that allows transmitting the data between parties as a JSON it is digitally signed so the information is trusted and verified JWT Token can be signed using secret (with HMAC Algorithm) or with the public or private key pairs using RSA Or ECDSA JWT Token Authentication is very popular in Website Development.
In this article, we have implemented the JWT Token Authentication using Three Tier Architecture
Download Code
If you want to down the complete project, then follow this link.