Securing a web application is one of the most important jobs to do and usually one of the hardest things to pull off. In this series, we are going to learn how to implement JWT authentication in ASP.Net Core Web API on the server-side and Angular on the client side.

We are also going to learn how authentication works in general and how to utilize JSON web tokens to securely transmit the user’s credentials from the server to the client and vice versa.


VIDEO: ASP.NET Core Authentication with JWT and Angular - Part 1.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!


Therefore, we are going to divide this series into three parts. In the first part, we are going to implement a JWT authentication in ASP.NET Core Web API and see how the integration process works between Web API and JWT (JSON web token). In the second part, we are going to implement front-end features like login, logout, securing routes, and role-based authorization with Angular. Finally, in the third part of the series, we are going to learn about refresh tokens and their use in modern web applications.

To download the source code for this article, you can visit our GitHub repository.

So, let’s start.

The Big Picture of JWT Authentication

Before we get into the implementation of authentication and authorization, let’s have a quick look at the JWT authentication big picture. There is an application that has a login form. A user enters their username, and password and presses the login button. After pressing the login button, a client (eg web browser) sends the user’s data to the server’s API endpoint:web authentication big picture

When the server validates the user’s credentials and confirms that the user is valid, it’s going to send an encoded JWT to the client. JSON web token is basically a JavaScript object that can contain some attributes of the logged-in user. It can contain a username, user subject, user roles, or some other useful information.

On the client-side, we store the JWT in the browser’s storage to remember the user’s login session. We may also use the information from the JWT to enhance the security of our application as well.

What is JWT (JSON Web Token)

JSON web tokens enable a secure way to transmit data between two parties in the form of a JSON object. It’s an open standard and it’s a popular mechanism for web authentication. In our case, we are going to use JSON web tokens to securely transfer a user’s data between the client and the server.

JSON web tokens consist of three basic parts: the header, payload, and signature.

One real example of a JSON web token:

JWT example

Different token parts are shown with different colors:

Header

The first part of JWT is the Header, which is a JSON object encoded in the base64 format. The header is a standard part of JWT and we don’t have to worry about it. It contains information like the type of token and the name of the algorithm:

{ 
    "alg": "HS256", 
    "typ": "JWT" 
}

Payload

After the Header, we have a Payload which is also a JavaScript object encoded in the base64 format. The payload contains some attributes about the logged-in user. For example, it can contain the user id, user subject, and information about whether a user is an admin user or not. JSON web tokens are not encrypted and can be decoded with any base64 decoder so we should never include sensitive information in the Payload:

{ 
    "sub": "1234567890", 
    "name": "John Doe", 
    "iat": 1516239022 
}

Signature

Finally, we have the Signature part. Usually, the server uses the signature part to verify whether the token contains valid information – the information the server is issuing. It is a digital signature that gets generated by combining the header and the payload together. Moreover, it’s based on a secret key that only the server knows:

JWT signature composition

So, if malicious users try to modify the values in the payload, they have to recreate the signature and for that purpose, they need the secret key that only the server knows about. On the server side, we can easily verify if the values are original or not by comparing the original signature with a new signature computed from the values coming from the client.

So, we can easily verify the integrity of our data just by comparing the digital signatures. This is the reason why we use JWT.

Creating ASP.NET Core Web API Project

Now let’s create a brand new ASP.NET Core Web API project. We can create a new Web API project with .NET Core CLI or we can use Visual Studio. For this article, let’s use Visual Studio.

We can open the launchSettings.json file and modify the applicationUrl property:

"applicationUrl": "https://localhost:5001;http://localhost:5000"

Now, we can start our project.

As a result, we will see our application hosted at https://localhost:5001 and the browser automatically sends a GET request to /weatherforecast.

So far so good.

In the next step, we are going to configure JWT authentication in our application.

Configuring JWT Authentication

To configure JWT authentication in .NET Core, we need to modify Program.csfile. If you are using .NET Core version 5, you have to add the modifications in the Startup.cs file inside the ConfigureServices method.

For the sake of simplicity, we are going to add all the code inside the Program class. But the better practice is to use Extension methods so we could free our class from extra code lines. If you want to learn how to do that, and to learn more about configuring the .NET Core Web API project, check out: .NET Core Service Configuration.

First, let’s install the Microsoft.AspNetCore.Authentication.JwtBearer NuGet package that we require to work with JWT in the ASP.NET Core app:

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

Next, let’s add the code to configure JWT right above the builder.Services.AddControllers() line:

builder.Services.AddAuthentication(opt => {
    opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "https://localhost:5001",
            ValidAudience = "https://localhost:5001",
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"))
        };
    });

In order for this to work, we have to add a few using directives:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

JWT Configuration Explanation

Firstly, we register the JWT authentication middleware by calling the AddAuthentication method. Next, we specify the default authentication scheme JwtBearerDefaults.AuthenticationScheme as well as DefaultChallengeScheme.

By calling the AddJwtBearer method, we enable the JWT authenticating using the default scheme, and we pass a parameter, which we use to set up JWT bearer options:

  • The issuer is the actual server that created the token (ValidateIssuer=true)
  • The receiver of the token is a valid recipient (ValidateAudience=true)
  • The token has not expired (ValidateLifetime=true)
  • The signing key is valid and is trusted by the server (ValidateIssuerSigningKey=true)

Additionally, we are providing values for the issuer, audience, and the secret key that the server uses to generate the signature for JWT.

We are going to hardcode both username and password for the sake of simplicity. But, the best practice is to put the credentials in a database or a configuration file or to store the secret key in the environment variable.

There is one more step we need to do to make our authentication middleware available to the application:

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

And that’s all we need to configure the JWT authentication in ASP.NET Core. This is something we would do in the Configure method inside the Startup class if we were using the .NET 5 app.

Securing API Endpoints

We already have an API endpoint /weatherforecast to get some example weather information and that endpoint is not secure. Anyone can send a request to https://localhost:5001/weatherforecast to fetch the values. So, in this section, we are going to add a new api/customers endpoint to serve a list of the customers. This endpoint is going to be secure from anonymous users and only logged-in users will be able to consume it.

Now, let’s add an empty CustomersController in the Controllers folders. Inside the controller, we are going to add a Get action method that is going to return an array of customers. More importantly, we are going to add an extra security layer by decorating the action method with the[Authorize] attribute so only logged-in users can access the route:

[Route("api/[controller]")]
[ApiController]
public class CustomersController : ControllerBase
{
    [HttpGet, Authorize]
    public IEnumerable<string> Get()
    {
        return new string[] { "John Doe", "Jane Doe" };
    }
}

To be able to use the Authorize attribute we have to add a new using directive inside the file:

using Microsoft.AspNetCore.Authorization;

Authorize attribute on top of the GET method restricts access to only authorized users. Only users who are logged in can fetch the list of customers. Therefore, this time if we make a request to https://localhost:5001/api/customers from the browser’s address bar, instead of getting a list of customers, we are going to get a 401 Not Authorized response:

HTTP 401 Not Authorized response

Adding Login Endpoint

To authenticate anonymous users, we have to provide a login endpoint so the users can log in and access protected resources. A user is going to provide a username and password, and if the credentials are valid we are going to issue a JSON web token for the requesting client.

In addition, before we start implementing the authentication controller, we need to add a LoginModel to hold user’s credentials on the server. LoginModel is a simple class that contains two properties: UserName and Password.  We are going to create a Models folder in the root directory and inside it a LoginModel class:

public class LoginModel
{
    public string? UserName { get; set; }
    public string? Password { get; set; }
}

Also, let’s create one more class inside the same Models folder:

public class AuthenticatedResponse
{
    public string? Token { get; set; }
}

We will see in a minute why we need this class.

Now let’s create the AuthController inside the Controllers folder.

Inside the AuthControllerwe are going to add the Login action to validate the user’s credentials. If the credentials are valid, we are going to issue a JSON web token. For this demo, we are going to hardcode the username and password to implement a fake user. After validating the user’s credentials we are going to generate a JWT with a secret key. JWT uses the secret key to generate the signature.

So, let’s implement the AuthController:

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginModel user)
    {
        if (user is null)
        {
            return BadRequest("Invalid client request");
        }

        if (user.UserName == "johndoe" && user.Password == "def@123")
        {
            var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345"));
            var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
            var tokeOptions = new JwtSecurityToken(
                issuer: "https://localhost:5001",
                audience: "https://localhost:5001",
                claims: new List<Claim>(),
                expires: DateTime.Now.AddMinutes(5),
                signingCredentials: signinCredentials
            );

            var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);

            return Ok(new AuthenticatedResponse { Token = tokenString });
        }

        return Unauthorized();
    }
}

Login Action Explanation

We decorate our Login action with the HttpPost attribute. Inside the login method, we create the SymmetricSecretKey with the secret key value superSecretKey@345. Then, we create the SigningCredentials object and as arguments, we provide a secret key and the name of the algorithm that we are going to use to encode the token.

Here comes the interesting part.

The first two steps are the standard steps that we don’t need to worry about. The third step is the one that we are interested in. In the third step, we create the JwtSecurityToken object with some important parameters:

  • Issuer: The first parameter is a simple string representing the name of the webserver that issues the token
  • Audience: The second parameter is a string value representing valid recipients
  • Claims: The third argument is a list of user roles, for example, the user can be an admin, manager, or author (we are going to add roles in the next post)
  • Expires: The fourth argument is the DateTime object that represents the date and time after which the token expires

Then, we create a string representation of JWT by calling the WriteToken method on JwtSecurityTokenHandler. Finally, we return JWT in a response. As a response, we create the AuthenticatedResponse object that contains only the Token property.

Testing the JWT Authentication

Now, let’s start our application. Also, we are going to use Postman to send requests.

That said, let’s send a POST request to https://localhost:5001/api/auth/login and provide a request body:

{ 
    "UserName":"johndoe", 
    "Password": "def@123" 
}

In the response section, we are going to see a 200 OK response with the JWT string in the response body:Postam Jwt Response

In the articles to come, we are going to learn how to consume this token in an Angular application and additionally how to refresh a token in a web application.

Conclusion

In this post, we have learned more about security and how it is a crucial part of web application development. We talked about what JWT is and how it fits in the implementation of web security. Additionally, we’ve seen how to use the Authorize attribute to protect our action, and how to implement a Login action that issues a new JWT if a login is successful.

In the next part, we are going to implement a front-end side of our application with Angular. We are going to implement login, logout, authorization, secure the routes, and much more.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!