.NET 5 come with a JWT middleware services for validating and creating JWTs. Included below is sample code. See the official reference documentation if you need to learn more about a service or library.
appsettings.json
{
"jwtConfig": {
"Secret": "A super secret string that can be whatever you like"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Configuration
using JwtService.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
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;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JwtService
{
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();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "JwtService", Version = "v1" });
});
var secret = Encoding.ASCII.GetBytes(Configuration["JwtConfig:secret"]);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(secret),
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = "Issuer",
ValidAudience = "Audience",
ClockSkew = TimeSpan.Zero,
};
});
services.AddScoped<IApplicationUser, ApplicationUser>();
}
// 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.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JwtService v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Sample service
public interface IApplicationUser
{
string Authenticate(string username, string password);
LoginResponse AuthenticateMvc(string username, string password);
}
public class ApplicationUser : IApplicationUser
{
private readonly IConfiguration Configuration;
public ApplicationUser(IConfiguration configuration)
{
Configuration = configuration;
}
private List<User> _users = new List<User>
{
new User { Id = 1, FirstName = "Hello", LastName = "World", Username = "username", Password = "password" }
};
public string Authenticate(string username, string password)
{
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
// return null if user not found
if (user == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(Configuration["JwtConfig:secret"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString()),
}),
IssuedAt = DateTime.UtcNow,
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Issuer = "Issuer",
Audience = "Audience"
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
public LoginResponse AuthenticateMvc(string username, string password)
{
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
// return null if user not found
if (user == null)
return null;
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(Configuration["JwtConfig:secret"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString()),
}),
IssuedAt = DateTime.UtcNow,
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Issuer = "Issuer",
Audience = "Audience"
};
var token = tokenHandler.CreateToken(tokenDescriptor);
//Mock response
LoginResponse response = new LoginResponse()
{
token = tokenHandler.WriteToken(token),
role = "User",
claims = new List<ClaimDto>()
{
new ClaimDto() {type = ClaimTypes.Role, value = "UserRole" },
new ClaimDto() {type= ClaimTypes.Email, value = "email@email.com" }
}
};
return response;
}
}
Controller
[Authorize]
[ApiController]
[Route("[controller]")]
public class AccountController : ControllerBase
{
private readonly IApplicationUser _authService;
private readonly ILogger<AccountController> _logger;
public AccountController(IApplicationUser authSerice, ILogger<AccountController> logger)
{
_authService = authSerice;
_logger = logger;
}
//POST: /Account/Authenticate
[AllowAnonymous]
[HttpPost("authenticate")]
[ProducesResponseType(200, Type = typeof(string))]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public IActionResult Authenticate([FromBody] LoginModel model)
{
string token = _authService.Authenticate(model.Username, model.Password);
if (string.IsNullOrEmpty(token))
{
return BadRequest(new { message = "Username or password is incorrect" });
}
return Ok(token);
}
//POST: /Account/AuthenticateMvc
[AllowAnonymous]
[HttpPost("authenticatemvc")]
[ProducesResponseType(200, Type = typeof(LoginResponse))]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public IActionResult AuthenticateMvc([FromBody] LoginModel model)
{
LoginResponse result = _authService.AuthenticateMvc(model.Username, model.Password);
if (result == null || string.IsNullOrEmpty(result.token))
{
return BadRequest(new { message = "Username or password is incorrect" });
}
return Ok(result);
}
}