DotNet 7 JSON Web Token
Building Web Apps with .Net 7: A Comprehensive Guide to JSON Web Tokens
Recently, my colleague Michel and I embarked on a journey to construct a web application using C# with .Net 7. What initially seemed like a routine task became an exciting adventure, as .Net 7 presented a new landscape compared to .Net 3.5.
Setting Up the Environment
Regardless of our individual IDE preferences - JetBrains Ridder for me and Visual Studio for Michel - we created a new solution to tackle this project head-on. Our experience in varied programming languages such as Java, C#, Elm, TypeScript, Kotlin, and Elixir equipped us to tackle this new challenge.
Our plan? To separate the authentication API from the business API. To kick things off, we added a new ASP.NET Core web API project at the root of our solution using the command dotnet new webapi
.
Understanding .Net 7 Authentication
We soon discovered an intriguing change in .Net 7: there was no option for Individual Authentication without Azure. It was a reminder of the ever-evolving nature of .Net, and Microsoft’s clever strategies to retain their customers.
Determined, we decided to devise our own solution for authentication.
Creating a JWT in .Net 7
Building Domain Objects
The first step is to create a new web API project without authentication using the command:
dotnet new webapi -au None -n "SoCraDev.Lab.RapidScrum.Authentification"
Next, we build the model objects within a “domains” folder at the root of the project, including the User class that serves as our domain object and a UserRequest class to separate the DTO from the request and response.
namespace SoCraDev.Lab.RapidScrum.Token.Domain;
public class User
{
public string UserName { get; set; } = string.Empty;
public string PasswordHash { get; set; }
}
public class UserRequest
{
public required string UserName { get; set; }
public required string Password { get; set; }
}
Creating the Authentication Controller
We then establish the Authentication controller, where users can authenticate and obtain a token. This process requires adding libraries from NuGet, namely BCrypt.Net, which aids in string encryption.
In the AuthController, we hash the password using BCrypt and provide methods to register a user and verify login credentials.
[Route("api/[controller]")]
[ApiController]
public class AuthController : Controller
{
private readonly IConfiguration _configuration;
private static User _user = new();
public AuthController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpPost("Register")]
public ActionResult<User> Register(UserRequest userRequest)
{
string passwordHash = BCrypt.Net.BCrypt.HashPassword(userRequest.Password);
_user.UserName = userRequest.UserName;
_user.PasswordHash = passwordHash;
return Ok(_user);
}
[HttpPost("login")]
public ActionResult<User> Login(UserRequest userRequest)
{
if(_user.UserName != userRequest.UserName)
{
return Unauthorized();
}
if (!BCrypt.Net.BCrypt.Verify(userRequest.Password, _user.PasswordHash))
{
return Unauthorized();
}
return _user;
}
Generating JWT Tokens
The next step involves creating a method to generate authentication tokens. For this, we add libraries - Microsoft.IdentityModel.Tokens
and System.IdentityModel.Tokens.Jwt
- and a token key in the appsettings.json
file.
"AppSettings": {
"Token": "SuperSecretTokenSuperSecretTokenSuperSecretTokenSuperSecretToken"
}
We create a CreateToken method within the AuthController that constructs and returns a JWT.
private readonly IConfiguration _configuration;
public AuthController(IConfiguration configuration)
{
_configuration = configuration;
}
private string CreateToken(User user)
{
List<Claim> claims = new()
{
new Claim(ClaimTypes.Name, user.UserName)
};
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
_configuration
.GetSection("AppSettings:Token")
.Value!));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var token = new JwtSecurityToken(
issuer: "localhost",
audience: "localhost",
claims: claims,
expires: DateTime.Now.AddDays(1),
signingCredentials: credentials);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
return jwt;
}
We call the CreateToken method inside our Register method
[HttpPost("Register")]
public ActionResult<User> Register(UserRequest userRequest)
{
string passwordHash = BCrypt.Net.BCrypt.HashPassword(userRequest.Password);
_user.UserName = userRequest.UserName;
_user.PasswordHash = passwordHash;
string token = CreateToken(_user);
return Ok(token);
}
Running the API application now yields a 512-byte token.
If you paste your token on https://jwt.io, you will see the domain and the username.
And there you have it! A step-by-step guide to handling JSON Web Tokens in .Net 7.