In this article, we are going to create and validate JWT tokens and we are also going to use custom JWT middleware, so this will also cover the authentication API with ASP.NET Core 3.1.
we will create/generate a JWT token based on id(ex-UserId), that token will be used for authentication API and also we will get Id from that token so we can use that id for any operations(like getting data from id or storing id).
first, we need to create a project in ASP.NET Core 3.1.
we are going to use the NuGet package System.IdentityModel.Tokens.Jwt
.andJwtSecurityTokenHandler
is a class of this package which we are going to use to implement custom JWT.
so install the JWT Token Library via NuGet, following are different steps to install it.
.NET Core CLI: dotnet add package System.IdentityModel.Tokens.Jwt
Visual Studio Package Manager Console: System.IdentityModel.Tokens.Jwt
Goto NuGet Packages for Solution and search and install System.IdentityModel.Tokens.Jwt
Create a JWT Token in ASP.NET Core
public string GenerateJwtToken(int UserId) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes("[Your secret key or password which will be used to sign and verify JWT TOKENS]"); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim("id", UserId.ToString()) }), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); }
The above code will generate a JWT token with the specified UserId
as the "id"
claim, it means the token payload will contain the property "id": <UserId>
(e.g. "id": 1
).
Validate a JWT Token in ASP.NET Core
public int? ValidateJwtToken(string token) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes("[Your secret key or password which will be used to sign and verify JWT TOKENS]"); try { tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); var jwtToken = (JwtSecurityToken)validatedToken; var UserId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value); // if validation is successful then return UserId from JWT token return UserId; } catch { // if validation fails then return null return null; } }
The above code will try to validate the JWT token which we provide and return the UserId from the token claims. it will return null If validation fails.
Validate Tokens in Custom JWT Middleware
using Microsoft.AspNetCore.Http; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Text; using System.Threading.Tasks; using JwtDemoAPI.Helpers; namespace JwtDemoAPI.Middleware { public class JwtMiddleware { private TokenHelper _tokenHelper = new TokenHelper(); private readonly RequestDelegate _next; public JwtMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); if (token != null) { await attachAccountToContext(context, token); } await _next(context); } private async Task attachAccountToContext(HttpContext context, string token) { try { var UserId = _tokenHelper.ValidateJwtToken(token); // on successful jwt validation attach UserId to context context.Items["UserId"] = UserId; } catch { // if jwt validation fails then do nothing } } } }
Above is a custom JWT middleware that will validate the JWT token in the request “Authorization” header if it exists.
if JWT validation gets success then the middleware retrieves the associated UserId from the JWT token which we generated and assigns it to context.Items[“UserId”], this makes the current UserId available to any other code running within the current request scope. later we will use this in a custom authorize attribute.
Check Validated Token with Custom Authorize Attribute
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System; namespace JwtDemoAPI.Helpers { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeAttribute : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { var UserId = context.HttpContext.Items["UserId"]; if (UserId == null) { // not logged in context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized }; } } } }
Above is a basic custom authorize attribute which will check if the above custom JWT middleware succeeded by checking if an UserId was attached to the current HTTP context for the request (context.HttpContext.Items[“UserId”]).
it will simply check that if UserId exists in context.HttpContext.Items[“UserId”] if not then it will Unauthorize the request.
ASP.NET Core Startup Class
in startup class, we need to add UseMiddleware for adding custom JWT auth middleware.
following is Startup.cs, in theConfigureServices
method we need to add custom JWT auth middleware.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using JwtDemoAPI.Middleware; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace JwtDemoAPI { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); // global cors policy app.UseCors(x => x .SetIsOriginAllowed(origin => true) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); app.UseAuthorization(); // Custom JWT AUTH Middleware app.UseMiddleware<JwtMiddleware>(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
Note: make sure to add app.UseMiddleware<JwtMiddleware>(); before app.UseEndpoints.
Following are Screenshots of API calls from the postman.
1.GenrateToken API (this will genrate JWT Token based on id)
2.Get UserName API (this will authorize API with JWT token)
thank you