Asp Net Core 5 Rest API Authentication with JWT Step by Step
Tags: dotnet, api, beginners, jwt
Published: 2021-01-22 09:31:48

In this post i will be showing you How to add JWT authentication to our Asp.Net Core REST API 

Some of the topics we will cover are registration, login functionalities and utilising JWTs ("Json Web Tokens") and Bearer authentication.

You can also watch the full step by step video on YouTube:
{% youtube LgpC4tYtc6Y %}

As well download the source code:
https://github.com/mohamadlawand087/v7-RestApiNetCoreAuthentication

This is Part 2 of API dev series you can check the different parts by following the links:
- Part 1:https://dev.to/moe23/asp-net-core-5-rest-api-step-by-step-2mb6
- Part 3: https://dev.to/moe23/refresh-jwt-with-refresh-tokens-in-asp-net-core-5-rest-api-step-by-step-3en5

We will be basing our current work on our previous Todo REST API application that we have created in our last article (https://dev.to/moe23/asp-net-core-5-rest-api-step-by-step-2mb6).

You can follow along by either going through that article and building the application with me as we go or you can get the source code from github, https://github.com/mohamadlawand087/v6-RestApiNetCore5.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/asz47zt2t3mzqlz227c1.png)

Once we have our code ready lets get started.

The first thing we need to install some package to utilise authentication

```bash
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer 
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore 
dotnet add package Microsoft.AspNetCore.Identity.UI 
```

then we need to do is we need to update our appsettings.json, in our appsettings we will need to add a JWT settings section and within that settings we need to add a JWT secret

```json
"JwtConfig": {
    "Secret" : "ijurkbdlhmklqacwqzdxmkkhvqowlyqa"
  },
```

In order for us to generate our secret we are going to use a free web tool to generate a random 32 char string [https://www.browserling.com/tools/random-string](https://www.browserling.com/tools/random-string) 

After adding the randomly generate 32 char string in our app settings now we need to create a new folder in our root directory called configuration.

Inside this configuration folder we will create a new class called JwtConfig

```csharp
public class JwtConfig
{
    public string Secret { get; set; }
}
```

Now we need to update our startup class, inside our ConfigureServices method we need to add the below in order to inject our JwtConfiguration in our application

```csharp
services.Configure<JwtConfig>(Configuration.GetSection("JwtConfig"));
```

Adding these configuration in our startup class register the configurations in our [Asp.Net](http://asp.Net) core middlewear and in our IOC container.

The next step is adding and configuring authentication in our startup class, inside our ConfigureServices method we need to add the following

```csharp
// within this section we are configuring the authentication and setting the default scheme
services.AddAuthentication(options => {
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwt => {
    var key = Encoding.ASCII    .GetBytes(Configuration["JwtConfig:Secret"]);

    jwt.SaveToken = true;
    jwt.TokenValidationParameters = new TokenValidationParameters{
        ValidateIssuerSigningKey= true, // this will validate the 3rd part of the jwt token using the secret that we added in the appsettings and verify we have generated the jwt token
        IssuerSigningKey = new SymmetricSecurityKey(key), // Add the secret key to our Jwt encryption
        ValidateIssuer = false, 
        ValidateAudience = false,
        RequireExpirationTime = false,
        ValidateLifetime = true
    }; 
});

services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApiDbContext>();
```

After updating the ConfigureServices we need to update the Configure method by adding authentication

```csharp
app.UseAuthentication();
```

Once we add the configurations we need to build the application to see if everything is still building as it should.

```bash
dotnet run
dotnet build
```

The next step is to update our ApiDbContext to take advantage of the Identity provider that [Asp.Net](http://asp.Net) provide for us, will navigate to our ApiDbContext in the Data folder and we update the ApiDbContext class as the following

```csharp
public class ApiDbContext : IdentityDbContext
```

by inheriting from IdentityDbContext instead of DbContext, EntityFramework will know that we are using authentication and it will build the infrastructure for us to utilise the default identity tables. 

To Generate the identity tables in our database we need to prepare migrations scripts and run them. to do that inside the terminal we need to type the following

```csharp
dotnet ef migrations add "Adding authentication to our Api"
dotnet ef database update
```

Once our migrations is completed we can open our database app.db with Dbeaver and we can see that our identity tables has been created for us by Entity Framework

The next step will be to setup out controllers and build the registration process for the user. Inside out controller folder will need to create a controller and our DTOs (data transfer objects).

Will start by adding a new folder called Domain in our root directory, and we add a class called AuthResult

```csharp
public class AutResult
{
    public string Token {get;set;}
    public bool Result { get; set; }
		public List<string> Errors { get; set; }
}
```

Will start by adding some folders to organise our DTOs, inside the Models folder will add a folder called DTO and within the DTO folder will create 2 folders Requests/Responses 

We need to add the UserRegistrationRequestDto which will be used by our registration action in the Controller. Then will navigate to Models/DTO/Requests and add a new class called UserRegistrationRequestDto 

Models/Dto/Requests/UserRegistrationRequestDto.cs

```csharp
// For simplicity we are only adding these 3 feilds we can change it and make it as complex as we need
public class UserRegistrationRequestDto
{
    [Required]
    public string Name { get; set; }
    [Required]
    public string Email { get; set; }
    [Required]
    public string Password { get; set; }
}
```

Model/Dto/Response/RegistrationResponse.cs

```csharp
// We are inheriting from AuthResult class
public class RegistrationResponse : AuthResult
{
    
}
```

Now we need to add our user registration controller, inside our controller folder we add a new class we call it AuthManagementController and we update it with the code below

```csharp
[Route("api/[controller]")] // api/authmanagement
[ApiController]
public class AuthManagementController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly JwtConfig _jwtConfig;
    
    public AuthManagementController(UserManager<IdentityUser> userManager, IOptionsMonitor<JwtConfig> optionsMonitor)
    {
        _userManager = userManager;
        _jwtConfig = optionsMonitor.CurrentValue;
    }

    [HttpPost]
    [Route("Register")]
    public async Task<IActionResult> Register([FromBody] UserRegistrationRequestDto user)
    {
        // Check if the incoming request is valid
        if(ModelState.IsValid)
        {
            // check i the user with the same email exist
            var existingUser = await _userManager.FindByEmailAsync(user.Email);

            if(existingUser != null) 
            {
                return BadRequest(new RegistrationResponse() {
                                        Result = false,
                                        Errors = new List<string>(){
                                            "Email already exist"
                                        }});
            }

            var newUser = new IdentityUser(){Email = user.Email, UserName = user.Email};
            var isCreated = await _userManager.CreateAsync(newUser, user.Password);
            if(isCreated.Succeeded)
            {
                var jwtToken = GenerateJwtToken(newUser);

                return Ok(new RegistrationResponse() {
                        Result = true, 
                        Token = jwtToken
                });
            }

            return new JsonResult(new RegistrationResponse(){
                    Result = false,
                    Errors = isCreated.Errors.Select(x => x.Description).ToList()}
                    ) {StatusCode = 500};
        }
        
        return BadRequest(new RegistrationResponse() {
                                        Result = false,
                                        Errors = new List<string>(){
                                            "Invalid payload"
                                        }});
    }

		private string GenerateJwtToken(IdentityUser user)
    {
        // Now its ime to define the jwt token which will be responsible of creating our tokens
        var jwtTokenHandler = new JwtSecurityTokenHandler();

        // We get our secret from the appsettings
        var key = Encoding.ASCII.GetBytes(_jwtConfig.Secret);

        // we define our token descriptor
            // We need to utilise claims which are properties in our token which gives information about the token
            // which belong to the specific user who it belongs to
            // so it could contain their id, name, email the good part is that these information
            // are generated by our server and identity framework which is valid and trusted
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new []
            {
                new Claim("Id", user.Id),
                new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                new Claim(JwtRegisteredClaimNames.Email, user.Email),
                // the JTI is used for our refresh token which we will be convering in the next video
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            }),
            // the life span of the token needs to be shorter and utilise refresh token to keep the user signedin
            // but since this is a demo app we can extend it to fit our current need
            Expires = DateTime.UtcNow.AddHours(6),
            // here we are adding the encryption alogorithim information which will be used to decrypt our token
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha512Signature)
        };

        var token = jwtTokenHandler.CreateToken(tokenDescriptor);

        var jwtToken = jwtTokenHandler.WriteToken(token);

        return jwtToken;
    }
}
```

Once we finish the registration action we can now test it in postman and get the jwt token

So the next step will be creating the user login request. 

```csharp
public class UserLoginRequest
{
    [Required]
    public string Email { get; set; }
    [Required]
    public string Password { get; set; }
}
```

After that we need to add our login action in the AuthManagementControtller

```csharp
[HttpPost]
[Route("Login")]
public async Task<IActionResult> Login([FromBody] UserLoginRequest user)
{
    if(ModelState.IsValid)
    {
        // check if the user with the same email exist
        var existingUser = await _userManager.FindByEmailAsync(user.Email);

        if(existingUser == null) 
        {
            // We dont want to give to much information on why the request has failed for security reasons
            return BadRequest(new RegistrationResponse() {
                                    Result = false,
                                    Errors = new List<string>(){
                                        "Invalid authentication request"
                                    }});
        }

        // Now we need to check if the user has inputed the right password
        var isCorrect = await _userManager.CheckPasswordAsync(existingUser, user.Password);
        
        if(isCorrect)
        {
            var jwtToken = GenerateJwtToken(existingUser);

            return Ok(new RegistrationResponse() {
                    Result = true, 
                    Token = jwtToken
            });
        }
        else 
        {
             // We dont want to give to much information on why the request has failed for security reasons
            return BadRequest(new RegistrationResponse() {
                                    Result = false,
                                    Errors = new List<string>(){
                                         "Invalid authentication request"
                                    }});
        }
    }

    return BadRequest(new RegistrationResponse() {
                                    Result = false,
                                    Errors = new List<string>(){
                                        "Invalid payload"
                                    }});
}
```

now we can test it out and we can see that our jwt tokens has been generated successfully, the next step is to secure our controller, to do that all we need to do is add the Authorise attribute to the controller 

```csharp
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("api/[controller]")] // api/todo
[ApiController]
public class TodoController : ControllerBase
```

And now if we test it we are not able to execute any request since we are not authorised, in order for us to send authorised requests we need to add the authorisation header with the bearer token so that [Asp.Net](http://asp.Net) can verify it and give us permission to execute the actions

Thank you for taking the time and reading the article

This is Part 2 of API dev series you can check the different parts by following the links:
- Part 1:https://dev.to/moe23/asp-net-core-5-rest-api-step-by-step-2mb6
- Part 3: https://dev.to/moe23/refresh-jwt-with-refresh-tokens-in-asp-net-core-5-rest-api-step-by-step-3en5