Generating Json Web Tokens with .Net Core: A TDD Approach

What are Json Web Tokens?

JSON Web Tokens (JWTs) are secure, compact data structures used for the safe transmission of information between parties as JSON objects. This information is trusted because it's digitally signed, typically with a secret or a public/private key pair. A JWT is divided into three parts: the header (identifies token type and algorithm), payload (contains user and metadata "claims"), and signature (verifies sender and protects against tampering). JWTs are commonly used for user authentication and authorization in web applications, allowing secure access to services and resources.

In the example application we'll be exploring in this blog, we'll emulate the backend process that occurs after a user has successfully logged in. Specifically, we'll focus on how a JWT is generated using a symmetric key and returned to the user. This token serves as proof of authentication, allowing the user to access protected resources without the need for maintaining a traditional session on the server. For the purposes of this sample application, we're opting for a symmetric key approach, though in real-world scenarios, more complex key structures might be employed.

Lets Go!

This complete project will be available on my GitHub via the following link: However, if you'd like to follow along via my instructions, be my guest.

  1. In Visual Studio, create a empty web API project.
  2. Next, add a new test project to the root solution of the project. In this blog I am using the xUnit framework.
  3. Once the test project is added, don't forget to reference the original web API project created in the first step.

Token generation method unit test

Before we start writing out the unit test generating the JWT, it's important to outline and plan what the concrete implementation intends to do so that we can more easily write our test. The generation of the token solely relies on being created via the token handler wrapper class, which will be called via a method in the repository class. A wrapper class is a custom object designed to encapsulate or "wrap" another object, often from a third-party library, an external system, or built-in system classes. Its purpose is to provide a simplified or more suitable interface, extend or limit functionality, or offer a way to adapt or modify the behaviour of the wrapped object. Class wrappers are perfect for TDD as third party services can easily be mocked via its wrapped class. We will discuss mocking more in just a moment. We will now make a start on writing the unit test.

In line with the TDD approach, our initial test will be neutral since it's empty. But as we enhance and refine our test, we'll develop the handler wrapper class alongside it. This iterative process will see our tests fluctuating between passing and failing states:

public class JWTRepositoryTests
{
    [Fact]
    public void Generate_JWT_Token_Succesfully_Writes_Token()
    {

    }
}

Now that we have the blank test setup, we are going to introduce a mock of our JWT handler wrapper class using the Moq framework. But what exactly is mocking?

Mocking in TDD provides a way to isolate units of code, ensuring tests focus solely on the target functionality without being influenced by external dependencies. By using mocks, tests run faster, free from the latencies of real-world operations like database or network interactions. Mocks also grant control, allowing developers to simulate specific behaviors, including edge cases and error conditions, without any real-world side effects. This controlled environment ensures that tests accurately evaluate code behavior under a variety of scenarios, ensuring robustness and reliability.

In this context, we're mocking the JWT Handler wrapper class to ensure that when we test a method relying on the JWT Handler wrapper, we're evaluating only the logic of that method, not the inner workings of the JWT Handler concrete class itself. This distinction guarantees that any test outcome is attributed solely to the method we're assessing, not any unexpected behaviour from the JWT Handler:

public class JWTRepositoryTests
{
    private readonly Mock _jwtHandlerMock;
    
    public JWTRepositoryTests()
    {
        _jwtHandlerMock = new Mock();
    }

    [Fact]
    public void Generate_JWT_Token_Succesfully_Writes_Token()
    {
        //Arrange
        _jwtTokenHandlerMock.Setup(th => th.WriteToken(It.IsAny<SecurityToken())).Returns("fakeTokenString");
    }
}

At this stage, we've only set up the mock behavior for the WriteToken method, but haven't included an assertion in our test. This is intentional. As we proceed, our test will rely on a method from our repository class that interacts with the JWT handler wrapper. Once we introduce this method into our tests, we'll add the necessary assertions to validate its behavior in conjunction with the JWT handler.

The next step in our journey will be to implement the actual IJwtTokenHandler interface, class & method that adheres to it:

public class JWTHandlerWrapper : IJwtHandlerWrapper
{
    private readonly JwtSecurityTokenHandler _tokenHandler;
    
    public JWTHandlerWrapper()
    {
        _tokenHandler = new JwtSecurityTokenHandler();
    }
    
    public string WriteToken(SecurityToken token)
    {
        return _tokenHandler.WriteToken(token);
    }
}
public interface IJwtHandlerWrapper
{
    string WriteToken(SecurityToken token);
}

Having implemented the IJwtTokenHandler, our attention now shifts to the repository class. This class will feature a method dependent on the JWT handler wrapper for token generation, necessitating the inclusion of the wrapper class as a dependency. Moreover, to access the application's configuration properties, another dependency will be required, specifically for reading the secret symmetric key from the appsettings.json file. Although storing a symmetric key directly in the configuration is not a best practice for production apps, the underlying concept can be adapted to reference a URL (such as Azure Key Vault) where the symmetric key can be fetched securely. With the repository's arrangement in place, our next step in the TDD process is to update the unit test. This will involve the Arrange sequence where we set up our prerequisites, followed by the Act sequence, where we'll invoke the CreateToken method in the repository. Finally, we'll ensure our test covers the necessary assertions for the JWT generation method:

public class JWTRepositoryTests
{

    private readonly Mock _jwtHandlerMock;
    private readonly Mock _configurationMock;
    private readonly User _user;

    public JWTRepositoryTests()
    {
        _jwtHandlerMock = new Mock();
        _configurationMock = new Mock();
        _user = new User { Username = "TestUsername", Password = "TestPassword", Role = "testRole" };

        // Setup mock configuration values
        _configurationMock.SetupGet(x => x["Jwt:Secret"]).Returns("your-secret-value");
        _configurationMock.SetupGet(x => x["Jwt:Issuer"]).Returns("your-issuer-value");
        _configurationMock.SetupGet(x => x["Jwt:Audience"]).Returns("your-issuer-value");
    }

    [Fact]
    public void Generate_JWT_Token_Succesfully_Writes_Token()
    {
        //Arrange
        _jwtHandlerMock.Setup(th => th.WriteToken(It.IsAny())).Returns("fakeTokenString");
        var repository = new JWTRepository(_configurationMock.Object, _jwtTokenHandlerMock.Object);

        //Act
        var token = repository.GenerateToken(_user);

        //Assert
        Assert.Equal("fakeTokenString", token);
    }
}

In constructor of this test class, we have created a test mock user object. This design choice enhances test maintainability; by initializing the user once in the constructor, we allow the same user instance to be shared across multiple tests, preventing redundancy. In the remainder of the test class constructor, we've also setup mock configuration values which mimics any settings that are present in the appsettings.json file. When using a mocked instance of IConfiguration, such settings in appsettings.json will not be read, so it's important we remember to create theese mocked values. Moving onto the unit test itself, the Act phase involves calling the `GenerateToken` method, using the mock user as input. This emphasizes how a token might be generated for a user upon successful authentication. To validate the correctness of our method's behavior, the Assert phase confirms that the returned token matches our expectations. With this well-structured unit test, which leverages mocking to its advantage, we can now proceed to flesh out the concrete implementation of the repository class along with the user data strucutre class.

Repository class, method & user model implementation

public class JWTRepository
{
    private readonly IConfiguration _configuration;
    private readonly IJwtHandlerWrapper _jwtHandler;
    
    public JWTRepository(IConfiguration configuration, IJwtHandlerWrapper jwtHandler)
    {
        _configuration = configuration;
        _jwtHandler = jwtHandler;
    }
    
    public string GenerateToken(User user)
    {
        try
        {
            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Secret"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier,user.Username),
                new Claim(ClaimTypes.Role,user.Role)
            };
            
            var token = new JwtSecurityToken(_configuration["Jwt:Issuer"],
              _configuration["Jwt:Audience"],
              claims,
              expires: DateTime.UtcNow.AddSeconds(30),
              signingCredentials: credentials);
              
            var tokenString = _jwtHandler.WriteToken(token);
            
            return tokenString;
        }
        
        catch(Exception ex)
        {
            var error = ex.ToString();
            return null;
        }
    }
}
public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
    public string Role { get; set; }
}

That's it, we now have a passing unit test & the concrete implementation is also complete! The final functionality of the application is to create a controller endpoint with the purpose of authenticating a user. This controller method will call GenerateToken repository method and assign the token to the user upon successful authentication. Of course because this is a TDD approach, we will begin by writing out a failing unit test for the controller method & incrementally add to this unit test whilst we add to our concrete implementation of the controller method. Let us continue!

Controller authentication end-point unit test

Firstly, we will create a new class file under our test project and name it ControllerTests. This helps us keep our tests organised separating repository tests from controller tests as well as potential future integration tests. Before creating the unit test, we will discuss the desired behaviour of the concrete implementation of the controller test.

For demonstration purposes, the user's username and password will be passed to the controller' s method within the HTTP header. A pre-defined user will be set up in the appsettings.json file, so likewise to earlier, we will need to mock out these details along with IConfguration. Within this unit test, the repsiotry class will also need to be mocked out in order to assign the JWT to the user during authentication

To assess if the unit test passes or fails, we will be asserting if the controller method returns a status code of 200, meaning that the user has been successfully authenticated. Let's move onto the test implementation:

public class ControllerTests
{
    private Mock<IConfiguration> _mockConfiguration;
    private Mock<IJWTRepository> _mockJwtRepository;
    private readonly User _mockUser;
    
    public ControllerTests()
    {
        _mockConfiguration = new Mock();
        _mockJwtRepository = new Mock();
        _mockUser = new User { Username = "TestUsername", Password = "TestPassword", Role = "testRole" };

        _mockConfiguration.SetupGet(x => x["TestUsers:Username"]).Returns("TestUsername");
        _mockConfiguration.SetupGet(x => x["TestUsers:Password"]).Returns("TestPassword");
    }
    
    [Fact] 
    public async Task AuthenticateUser_Returns_OK()
    {
        //Arrange
        var jwtReppsitoryMock = new Mock();
        jwtReppsitoryMock.Setup(r => r.GenerateToken(_mockUser)).Returns("testToken");

        var controller = new JwtController(_mockConfiguration.Object, _mockJwtRepository.Object);
        controller.ControllerContext.HttpContext = new DefaultHttpContext();
        controller.HttpContext.Request.Headers["username"] = "TestUsername";
        controller.HttpContext.Request.Headers["password"] = "TestPassword";

        //Act
        var result = controller.AuthenticateUser();

        //Assert

        var objectResult = Assert.IsType(result);
        Assert.Equal(200, objectResult.StatusCode);
    }
}

With the above implementation, once we create the controller class file, we should have a passing unit test for successfully authenticating the user whilst simultaneously generating a JWT. Let's move onto the concrete controller class.

Controller class & authentication end-point method implementation

[Route("api/[controller]")]
[ApiController]
public class JwtController : ControllerBase
{
    private readonly IJWTRepository _jwtRepository;
    private readonly IConfiguration _configuration;
    
    public JwtController(IConfiguration configuration, IJWTRepository jWTRepository)
    {
        _configuration = configuration;
        _jwtRepository = jWTRepository;
    }
    
    [HttpPost("/AuthenticateUser")]
    public IActionResult AuthenticateUser()
    {
        string httpHeaderUsername = Request.Headers["username"].FirstOrDefault();
        string httpHeaderPassword = Request.Headers["password"].FirstOrDefault();

        User user = new User
        {
            Username = httpHeaderUsername,
            Password = httpHeaderPassword,
            Role = "User"
        };
        try
        {
            string username = _configuration["TestUsers:Username"];
            string password = _configuration["TestUsers:Password"];

            if (httpHeaderUsername == username && httpHeaderPassword == password)
            {
                var token = _jwtRepository.GenerateToken(user);
                return Ok(token);
            }
            else
            {
                return StatusCode(401, "Incorrect credentials");
            }
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
}

That's it! Now that the concrete implementation is complete, we can manually test out the user authentication along with the JWT generation using an API tool such as Postman. Set the URL to POST and add the localhost ULR along with the port your application is running on. You can pass in the username and password in the HTTP headers section. Upon successful authentication, you will get a 200 OK code response along with a token. See the below image:

Right now, this brings us to the end of this blog, but part 2 will be coming soon where will also test for unsuccessful operations when generating the JWT along with adding integration tests. Sit tight!