I'm using UserService class of GetById method to actually Get user details by id.
using Custom_Jwt_Token_Example.Models;
namespace Custom_Jwt_Token_Example.Services
{
public class UserService : IUserService
{
private List<User> _users = new List<User> {
new User {
Id = 1, FirstName = "mytest",Role= new List<Role>{Role.Customer}, LastName = "User", Username = "mytestuser", Password = "test123"
},
new User {
Id = 2, FirstName = "mytest2", LastName = "User2", Username = "test", Password = "test"
}
};
public IEnumerable<User> GetAll()
{
return _users;
}
public User GetById(int id)
{
return _users.FirstOrDefault(x => x.Id == id);
}
}
}
Create a class called AuthenticationService in the Service Folder with the following code:
using Custom_Jwt_Token_Example.Helper;
using Custom_Jwt_Token_Example.Models;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Custom_Jwt_Token_Example.Services
{
public class AuthenticationService : IAuthenticationService
{
private List<User> _users = new List<User> {
new User {
Id = 1, FirstName = "mytest", LastName = "User", Username = "mytestuser",Role= new List<Role>{Role.Customer} , Password = "test123"
}
};
private readonly AppSettings _appSettings;
public AuthenticationService(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings.Value;
}
public AuthenticateResponse Authenticate(AuthenticateRequest model)
{
var user = _users.SingleOrDefault(x => x.Username == model.UserName && x.Password == model.Password);
if (user == null) return null;
var token = generateToken(user);
return new AuthenticateResponse() {Token= token};
}
private string generateToken(User user)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Key));
var credetial = new SigningCredentials(securityKey,SecurityAlgorithms.HmacSha256);
List<Claim> claims = new List<Claim>(){
new Claim("Id",Convert.ToString(user.Id)),
new Claim(JwtRegisteredClaimNames.Sub, "Test"),
new Claim(JwtRegisteredClaimNames.Email, "[email protected]"),
//new Claim("Role", Convert.ToString(user.Role)),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
foreach (var role in user.Role) {
claims.Add(new Claim("Role", Convert.ToString(role)));
}
var token = new JwtSecurityToken(_appSettings.Issuer, _appSettings.Issuer, claims, expires: DateTime.UtcNow.AddHours(1), signingCredentials: credetial);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
}
First, let's create a new class for our middleware component. In this example, let's assume we want to get a token from the header, validate the token, and attach it to the context of the user variable stored user information.
using Custom_Jwt_Token_Example.Services;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
namespace Custom_Jwt_Token_Example.Helper
{
public class JwtMiddleware
{
private readonly RequestDelegate _next;
private readonly AppSettings _appSettings;
public JwtMiddleware(RequestDelegate _next, IOptions<AppSettings> _appSettings)
{
this._next = _next;
this._appSettings = _appSettings.Value;
}
public async Task Invoke(HttpContext context, IUserService userService)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
//Validate Token
attachUserToContext(context, userService, token);
_next(context);
}
private void attachUserToContext(HttpContext context, IUserService userService, string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Key));
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ClockSkew = TimeSpan.Zero,
ValidIssuer = _appSettings.Issuer,
ValidAudience = _appSettings.Issuer
}, out SecurityToken validateToken);
var jwtToken = (JwtSecurityToken)validateToken;
var userId = int.Parse(jwtToken.Claims.FirstOrDefault(_=>_.Type=="Id").Value);
context.Items["User"] = userService.GetById(userId);
}
catch (Exception ex)
{
}
}
}
}
When decorating an API endpoint with the [Authorization] attribute, the OnAuthorization method will be called each time before API Endpoint is called.
In this scenario, Check user credentials as well as Role Validation. It returns Unathorization with a 401 status code. If the user does not exist and role is matched with the Authorize attribute
using Custom_Jwt_Token_Example.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Custom_Jwt_Token_Example.Helper
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class Authorization : Attribute, IAuthorizationFilter
{
private readonly IList<Role> _roles;
public Authorization(params Role[] _roles) {
this._roles = _roles?? new Role[]{ };
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var isRolePermission = false;
User user = (User)context.HttpContext.Items["User"];
if (user == null)
{
context.Result = new JsonResult(
new { Message = "Unauthorization" }
)
{ StatusCode = StatusCodes.Status401Unauthorized };
}
if(user != null && this._roles.Any())
foreach (var userRole in user.Role)
{
foreach (var AuthRole in this._roles)
{
if (userRole == AuthRole)
{
isRolePermission = true;
}
}
}
if(!isRolePermission)
context.Result = new JsonResult(
new { Message = "Unauthorization" }
)
{ StatusCode = StatusCodes.Status401Unauthorized };
}
}
}
Add Decorator an API endpoint with [Authorization(Role.Customer)] attribute.
using Custom_Jwt_Token_Example.Helper;
using Custom_Jwt_Token_Example.Models;
using Microsoft.AspNetCore.Mvc;
namespace Custom_Jwt_Token_Example.Controllers
{
[Authorization(Role.Customer)]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
In Appsetting.Json File, add Appsetting Section JWT Token Key and Issuer
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AppSettings": {
"Key": "986ghgrgtru989ASdsaerew13434545435",
"Issuer": "TestIssuer"
}
}