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.

Building Web Apps with
In our previous post-.Net 7 & JWT, we explained how to create a JSON Web Token. This article explores authorization via web tokens in .Net 7. It would be best if you were authenticated to access this Controller…