Authenticate using JWT
Install package Microsoft.AspNetCore.Authentication.JwtBearer
8.0.14
In EndpointConfigurator.cs
csharp
public static class EndpointConfigurator
{
public static void ConfigureJwt(this WebApplicationBuilder builder)
{
var jwtSettings = builder.Configuration.Get<AppSettings>().Jwt;
if (jwtSettings == null)
{
throw new Exception("appsettings.json missing JWT");
}
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()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidIssuer = jwtSettings.Issuer,
ValidAudience = jwtSettings.Audience,
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Secret)),
ClockSkew = TimeSpan.Zero,
};
});
}
}
In Program.cs, call ConfigureJwt()
csharp
builder.ConfigureJwt();
builder.Services.AddControllers();
//...
app.UseAuthentication(); //order matters
app.UseAuthorization();
app.MapControllers();
In Utilities, add TokenHelper.cs
csharp
public interface ITokenHelper
{
string GenerateToken(long userId);
ActionTaker GetActionTaker();
}
public class TokenHelper : ITokenHelper
{
private readonly AppSetting.JwtSetting _jwtSettings;
private readonly ILogger<TokenHelper> _logger;
private readonly IHttpContextAccessor _httpContextAccessor;
public TokenHelper(ILogger<TokenHelper> logger, IConfiguration configuration,
IHttpContextAccessor httpContextAccessor)
{
_jwtSettings = configuration.Get<AppSetting>().Jwt;
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
public string GenerateToken(long userId)
{
var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Secret));
var customClaims = new List<Claim> { new Claim("sub", userId.ToString()) };
var token = new JwtSecurityToken(
issuer: _jwtSettings.Issuer,
audience: _jwtSettings.Audience,
expires: DateTime.Now.AddMinutes(600),
signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256),
claims: customClaims
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public ActionTaker GetActionTaker()
{
StringValues? authorization = _httpContextAccessor.HttpContext?.Request.Headers["Authorization"];
if (string.IsNullOrEmpty(authorization.ToString()))
{
_logger.LogError("TokenHelper.GetUserId - Request header doesn't have Authorization");
throw new AuthenticationException("Unauthorized");
}
var jwtToken = authorization.ToString().Split(" ")[1];
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Secret)),
ClockSkew = TimeSpan.Zero
};
var principal = new JwtSecurityTokenHandler().ValidateToken(jwtToken, validationParameters, out var validatedToken);
var claims = ((JwtSecurityToken)validatedToken).Claims;
var sub = claims.FirstOrDefault(c => c.Type == "sub");
if (sub == null)
{
_logger.LogError("TokenHelper.GetUserId - Token does not have sub claim");
throw new AuthenticationException("Unauthorized");
}
if (long.TryParse(sub.Value, out long userId) == false)
{
_logger.LogError("TokenHelper.GetUserId - Token does not have sub claim");
throw new AuthenticationException("Unauthorized");
}
return new ActionTaker(userId);
}
}
In Controllers folder, add LoginController.cs
csharp
[ApiController]
[Route("[controller]")]
public class LoginController : Controller
{
private readonly ITokenHelper _tokenHelper;
public LoginController(ITokenHelper tokenHelper)
{
_tokenHelper = tokenHelper;
}
[HttpPost]
public IActionResult Login()
{
return Ok(_tokenHelper.GenerateToken(1));
}
}
In DependencyConfigurator.cs,
csharp
private static void RegisterHelpers(this WebApplicationBuilder builder)
{
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<ITokenHelper, TokenHelper>();
//...
}
In BookController.cs, inject ITokenHelper
, add [Authorize]
and use _tokenHelper.GetActionTaker();
to get userId
csharp
public class BookController : Controller
{
private readonly ILogger<BookController> _logger;
private readonly ITokenHelper _tokenHelper;
public BookController(ILogger<BookController> logger, ITokenHelper tokenHelper)
{
_logger = logger;
_tokenHelper = tokenHelper;
}
[Authorize]
[HttpPost]
public IActionResult Create(CreateBookRequest request)
{
var actionTaker = _tokenHelper.GetActionTaker();
_logger.LogInformation("BookController.Create - Request {@request}, userId {userId}", request, actionTaker.UserId);
//...
Call log in to get JWT token
###
POST {{Library_HostAddress}}/login/
Content-Type: application/json
Call API without token will get 401 unauthorized
GET {{Library_HostAddress}}/fact/
Content-Type: application/json
With token
GET {{Library_HostAddress}}/fact/
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoxNzQyNjU5ODM2LCJpc3MiOiJodHRwczovL2NvZGVoZXNpdmUuZGV2LyIsImF1ZCI6Imh0dHBzOi8vY29kZWhlc2l2ZS5kZXYvIn0.zM1bFPIRSuMr4SxACLBf33O8e_3mvo15avl-zONcOOE