Introduction
This article explains how to log in with JWT (json web token) and set custom authentication for every role logged in into the web application in ASP.NET Core 3.1.
So, for login, we will see how to get a JWT token with user claims and store it in the session storage key “JWToken”, then apply the authentication filter by Role, assign it to that user and restrict to another user unauthorized user and how to logout users.
Overview
JWT-(Json Web Token)
Authentication- Authentication is the process of determining who you are.
Claims-based authentication
Claims are a statement about or a property of a particular identity. That consists of name and value for ex. You could have UserName claim, EmailAddress claim, or Role claim.
- This is a single identity that contains a no. of claims.
Let’s start with an example:
Step 1
Create an ASP.NET Core web application with MVC in .NET Core 3.1.
Step 2
Create Login Controller.cs and make a login view over the index action.
- public class LoginController : BaseController
- {
- ApplicationDbContext db;
- public LoginController(ApplicationDbContext db)
- {
- this.db = db;
- }
- [HttpGet, AllowAnonymous]
- public IActionResult Index()
- {
- return View();
- }
- }
Step 3
Create a BaseController.cs and create an asynced "CreateAuthenticationTicket " function.
- public class BaseController : Controller
- {
- public async Task CreateAuthenticationTicket(AdminLogin user)
- {
- var key = Encoding.ASCII.GetBytes(SiteKeys.Token);
- var JWToken = new JwtSecurityToken(
- issuer: SiteKeys.WebSiteDomain,
- audience: SiteKeys.WebSiteDomain,
- claims: GetUserClaims(user),
- notBefore: new DateTimeOffset(DateTime.Now).DateTime,
- expires: new DateTimeOffset(DateTime.Now.AddDays(1)).DateTime,
- signingCredentials: new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
- );
-
- var token = new JwtSecurityTokenHandler().WriteToken(JWToken);
- HttpContext.Session.SetString("JWToken", token);
- }
-
-
- private IEnumerable<Claim> GetUserClaims(AdminLogin user)
- {
- List<Claim> claims = new List<Claim>();
- Claim _claim;
- _claim = new Claim(ClaimTypes.Name, user.UserName);
- claims.Add(_claim);
-
- _claim = new Claim("Role", Role.Admin);
- claims.Add(_claim);
-
- return claims.AsEnumerable<Claim>();
- }
- }
Create a Role struct in BaseController.cs and set different roles under this.
- public struct Role
- {
- public const string Admin = "Admin";
- public const string Guest = "Guest";
- }
Create Sitekeys.cs class for getting configurations from the appsettings.json file
SiteKeys.cs
- using Microsoft.Extensions.Configuration;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
-
- namespace DemoIntro.Models
- {
- public class SiteKeys
- {
- private static IConfigurationSection _configuration;
- public static void Configure(IConfigurationSection configuration)
- {
- _configuration = configuration;
- }
-
- public static string WebSiteDomain => _configuration["WebSiteDomain"];
- public static string Token => _configuration["Secret"];
-
- }
- }
Step 4
Now create the Login Post method
- [HttpPost, AllowAnonymous]
- public IActionResult LoginNow(AdminLogin model)
- {
- var admin = db.AdminLogin.Where(x => x.UserName.Trim() == model.UserName.Trim() && x.Password ==model.Password).FirstOrDefault();
- try
- {
- if (admin != null)
- {
- _ = CreateAuthenticationTicket(admin);
-
- return RedirectToAction(nameof(ProfileController.CreateEditProfile), "Profile");
- }
- else
- {
-
- return View("Index", model);
- }
- }
- catch (Exception ex)
- {
-
- return View("Index", model);
- }
- }
Step 5
Create index.cshtml
- @model DemoIntro.Data.Tables.AdminLogin
- @{
- Layout = null;
- }
-
- <!DOCTYPE html>
- <html>
- <head>
-
- </head>
- <body class="hold-transition login-page">
- <div class="login-box">
- <div class="card">
- <div class="card-body login-card-body">
- <p class="login-box-msg">Sign in to start your session</p>
-
- <form id="frmLogin" asp-action="LoginNow" asp-controller="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
-
-
- <div class="form-group">
- <div class="input-group mb-3">
- <input asp-for="@Model.UserName" class="form-control" placeholder="Email">
- <div class="input-group-append">
- <div class="input-group-text">
- <span class="fas fa-envelope"></span>
- </div>
- </div>
- </div>
- <span asp-validation-for="@Model.UserName" class="text-danger"></span>
- </div>
-
-
- <div class="form-group">
- <div class="input-group mb-3">
- <input asp-for="@Model.Password" class="form-control" placeholder="Password">
- <div class="input-group-append">
- <div class="input-group-text">
- <span class="fas fa-lock"></span>
- </div>
- </div>
- </div>
- <span asp-validation-for="@Model.Password" class="text-danger"></span>
- </div>
-
-
- <div class="row">
-
- <!-- /.col -->
- <div class="col-4">
- <button type="submit" class="btn btn-primary btn-block">Sign In</button>
- </div>
- <!-- /.col -->
- </div>
- </form>
- </div>
- <!-- /.login-card-body -->
- </div>
- </div>
- </body>
- </html>
Step 6
Set a few JWT authentication tags in Startup.cs file. services.AddAuthentication ,services.AddSession
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDbContext<ApplicationDbContext>(options =>
- options.UseSqlServer(
- Configuration.GetConnectionString("DefaultConnection")));
- services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
- .AddEntityFrameworkStores<ApplicationDbContext>();
- services.AddControllersWithViews();
- services.AddRazorPages();
-
-
- #region "JWT Token For Authentication Login"
- SiteKeys.Configure(Configuration.GetSection("AppSettings"));
- var key = Encoding.ASCII.GetBytes(SiteKeys.Token);
-
- services.AddSession(options =>
- {
- options.IdleTimeout = TimeSpan.FromMinutes(60);
- });
-
-
- services.AddAuthentication(auth =>
- {
- auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
- auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
- })
- .AddJwtBearer(token =>
- {
- token.RequireHttpsMetadata = false;
- token.SaveToken = true;
- token.TokenValidationParameters = new TokenValidationParameters
- {
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(key),
- ValidateIssuer = true,
- ValidIssuer = SiteKeys.WebSiteDomain,
- ValidateAudience = true,
- ValidAudience = SiteKeys.WebSiteDomain,
- RequireExpirationTime = true,
- ValidateLifetime = true,
- ClockSkew = TimeSpan.Zero
- };
- });
-
- #endregion
-
- }
Add JWToken in Authorization Bearer in the request header:
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- app.UseDatabaseErrorPage();
- }
- else
- {
- app.UseExceptionHandler("/Home/Error");
-
- app.UseHsts();
- }
- app.UseHttpsRedirection();
- app.UseStaticFiles();
- app.UseRouting();
-
- #region "JWT Token For Authentication Login"
-
- app.UseCookiePolicy();
- app.UseSession();
- app.Use(async (context, next) =>
- {
- var JWToken = context.Session.GetString("JWToken");
- if (!string.IsNullOrEmpty(JWToken))
- {
- context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
- }
- await next();
- });
- app.UseAuthentication();
- app.UseAuthorization();
-
-
- #endregion
-
- app.UseEndpoints(endpoints =>
- {
-
- endpoints.MapAreaControllerRoute(
- name: "routeArea",
- areaName: "Admin",
- pattern: "Admin/{controller=Home}/{action=Index}/{id?}");
- endpoints.MapRazorPages();
-
-
- endpoints.MapControllerRoute(
- name: "default",
- pattern: "{controller=Main}/{action=Index}/{id?}");
- endpoints.MapRazorPages();
- });
-
- }
Step 7
Add connection and configurations in the appsettings.json file.
- {
- "ConnectionStrings": {
-
- "DefaultConnection": "Server=LOKESH007\\LOKESH007;Database=DemoIntro;user=sa;password=admin@1234;Trusted_Connection=False;"
-
- },
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- },
- "AllowedHosts": "*",
-
- "AppSettings": {
- "Secret": "WriteMySecretKeyForAuthentication",
- "WebSiteDomain": "http://localhost/Demo/",
- "WebSiteName": "khushbu Saini"
- }
-
- }
Step 8
Now create our custom Authorize filter validating each user role, restrict unauthorized users, auto redirect to login if session timeout or unauthorized request hit.
Authorize.cs
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.Mvc.Filters;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Security.Claims;
- using System.Threading.Tasks;
-
- namespace DemoIntro.filters
- {
- public class AuthorizeAttribute : TypeFilterAttribute
- {
- public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
- {
- Arguments = new object[] { claim };
- }
- }
-
- public class AuthorizeFilter : IAuthorizationFilter
- {
- readonly string[] _claim;
-
- public AuthorizeFilter(params string[] claim)
- {
- _claim = claim;
- }
-
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
- var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;
-
- if (IsAuthenticated)
- {
- bool flagClaim = false;
- foreach (var item in _claim)
- {
- if (context.HttpContext.User.HasClaim("Role", item))
- flagClaim = true;
- }
- if (!flagClaim)
- {
- if (context.HttpContext.Request.IsAjaxRequest())
- context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
- else
- context.Result = new RedirectResult("~/Login/Index");
- }
- }
- else
- {
- if (context.HttpContext.Request.IsAjaxRequest())
- {
- context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
- }
- else
- {
- context.Result = new RedirectResult("~/Login/Index");
- }
- }
- return;
- }
- }
- }
Step 9
Set the access role to each user controller
- [Authorize(Role.Admin)]
- public class AchievementsController : BaseController
- {
-
- }
- [Authorize(Role.Guest)]
- public class AccessoriesController : BaseController
- {
- }
Step 10
Finally, the logout function for each use logout is here:
- [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
- public IActionResult Logout()
- {
- HttpContext.Session.Clear();
- return RedirectToAction("Index");
- }
Output
Here is the login page, I have the first login with the admin. It shows me successful login and redirects to the admin dashboard.
Now, click on Accessories Controller. Because I have provided the access of this controller to Guest only, the admin can't unable to access it and auto-redirect to login view.
Summary
In this article, I discussed how we can get log in with multiple identity claim users. We also saw how we create our custom authentication filter so that we can custom authenticate each user while logging in and also validate that user role. Furthermore, we saw how to restrict other unauthorized users for login and also restrict them with URL rewriting. At last, we saw how to log out.