How to Implement JWT in Core API and Angular – Part 1

  1. How to Implement JWT in Core API and Angular – Part 1
  2. How to Implement JWT in Core API and Angular — Part 2

1. Introduction

JWT means JSON Web Tokens, it usually uses for API authorization with the client. JWT is an open standard that defines a way to transmit information between two parties securely as a JSON object in a compact and verifiable way. It is great for securing REST APIs and authorization. There are some common use cases of JWT include:

1) Authorization – Once the user is logged in, each subsequent request will include the JWT to allow access to routes, services, and resources.

2) Information Exchange – JWT is a good way of securely transmitting information between parties.

Do you want to be a good trading in cTrader?   >> TRY IT! <<

3) User information storage – JWT can store user details, preferences, etc in a web/mobile application.

The benefits of using JWT include compact size, url-safe, ability to be encrypted, decentralized, suitable for mobile apps, and more.

And in this post, I will show you how to use JWT for .Net Core API and Angular.

2. The flow for using JWT

Let’s take a look at the flow for how’s the JWT working:

1) Client calls the API for a login request
2) Server(API) side generates JWT after login success
3) Pass the JWT to client-side
4) Client-side needs to save the JWT
5) Client passes the JWT to the API for each request
6) Server-side needs to verify the JWT from the client’s request, if successful then return the data otherwise return auth failed message.

3. Generate the JWT

We can create a JWT service to help to do that! We will use JwtSecurityToken to generate the JWT, this method under System.IdentityModel.Tokens.Jwt namespace, let’s take a look at the signature for this method. There are 4 overwrite methods for this, but we will use the below

public JwtSecurityToken(string issuer = null, string audience = null, 
                IEnumerable<Claim> claims = null, DateTime? notBefore = null, DateTime? 
                expires = null, SigningCredentials signingCredentials = null);

suppose we can let the parameters be null, but for safer, we will use all of them. We can put these parameters into appsettings.json so that can be control

"JwtSettings": {
    "SecurityKey": "MyVeryOwnSecurityKey",
    "Issuer": "MyVeryOwnIssuer",
    "Audience": "https://localhost:4810",
    "ExpirationTimeInMinutes": 1440
  },

and use them to create the token

var jwtOptions = new JwtSecurityToken(
             issuer: _configuration["JwtSettings:Issuer"],
             audience: _configuration["JwtSettings:Audience"],
             claims: GetClaims(user),
             expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                     _configuration["JwtSettings:ExpirationTimeInMinutes"])),
             signingCredentials: GetSigningCredentials());

the complete codes for the JwtService as below

using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using MyDemo.Core.Data.Entity;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace MyDemo.Core.Services;

public class JwtService
{
    private readonly IConfiguration _configuration;
    public JwtService(IConfiguration configuration)
    {
        //inject the configuration for getting the setting values
        _configuration = configuration;
    }
    public JwtSecurityToken GetToken(User user)
    {
        //create the Jwt token from setting values
        var jwtOptions = new JwtSecurityToken(
                        issuer: _configuration["JwtSettings:Issuer"],
                        audience: _configuration["JwtSettings:Audience"],
                        claims: GetClaims(user),
                        expires: DateTime.Now.AddMinutes(Convert.ToDouble(
                                _configuration["JwtSettings:ExpirationTimeInMinutes"])),
                        signingCredentials: GetSigningCredentials());
        return jwtOptions;
    }
    //generate signing credentials
    private SigningCredentials GetSigningCredentials()
    {
        var key = Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecurityKey"]);
        var secret = new SymmetricSecurityKey(key);
        return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
    }
    //create the claim base current user name and email
    private List<Claim> GetClaims(User user)
    {
        var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email) };
        return claims;
    }
}

4. Create the Login function

We need to create an API login function to return the JWT to the client.

4.1 Create the DTO

Before we start, we need to receive the login request object and a result object for return to the client, so we need to create two DTOs for the login function. In this way, I will simply introduce what’s DTO.

DTO stands for Data Transfer Object. It is a design pattern used to transfer data between different layers of an application, such as between the UI layer and the business logic layer. The main purpose of a DTO is to transfer data, so they don’t contain any behavior except for storage, retrieval, serialization, and deserialization of data.

We can create a DTO folder under the Data folder and create the LoginRequest.cs in this folder

//MyDemo.Core/Data/DTO/LoginRequest.cs

namespace MyDemo.Core.Data.DTO;

public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; } 
}

and the result object for returning the token

//MyDemo.Core/Data/DTO/LoginToken.cs

namespace MyDemo.Core.Data.DTO;

public class LoginToken
{
    /// <summary>
    /// The JWT token if the login attempt is successful, or NULL if not
    /// </summary>
    public string? access_token { get; set; }
    public string? user_id { get; set; }
    public string? token_type { get; set; }
    /// <summary>
    /// expires time in seconds
    /// </summary>
    /// <value></value>
    public int? expires_in { get; set; }
}

4.2. Create the Auth API

Create the AuthController to handle the login function, and we need to inject the below instance first

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IUserRepository _userRepository;
    private readonly JwtService _jwtService;
    private readonly IConfiguration _configuration;
    private readonly ILogger<AuthController> _logger;

    public AuthController(IUserRepository userRepository,
                        JwtService jwtService,
                        IConfiguration configuration,
                        ILogger<AuthController> logger)
    {
        _userRepository = userRepository;
        _jwtService = jwtService;
        _configuration = configuration;
        _logger = logger;
    }
}

create the login API to receive a LoginRequest

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest loginRequest)
{
    //todo...
}

check the user name and password for login, if successful then generate the token, otherwise, return the null data.

var user = await _userRepository.GetItemWithConditionAsync(
    u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
if (user == null)
{
    return Unauthorized(new LoginToken()
    {
        access_token = null,
        user_id = null,
        expires_in = 0,
        token_type = "Bearer"
    });
}
//generate a token by user information and appsettings values
var secToken = _jwtService.GetToken(user);
//serializes the token into a JWT format
var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
//get the expiration times and pass them to the client
var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
return Ok(new LoginToken()
{
    access_token = jwt,
    user_id = user.Id.ToString(),
    expires_in = expires_sec,
    token_type = "Bearer"
});

by the way, this is only for a demo, so I didn’t encrypt the password, you should encrypt it in a real project.

and we should use try...catch to handle exceptions in a complete function

[HttpPost("Login")]
public async Task<IActionResult> Login(LoginRequest loginRequest)
{
    try
    {
        var user = await _userRepository.GetItemWithConditionAsync(
            u => u.Name == loginRequest.Username && u.Password == loginRequest.Password);
        if (user == null)
        {
            return Unauthorized(new LoginToken()
            {
                access_token = null,
                user_id = null,
                expires_in = 0,
                token_type = "Bearer"
            });
        }
        //generate a token by user information and appsettings values
        var secToken = _jwtService.GetToken(user);
        //serializes the token into a JWT format
        var jwt = new JwtSecurityTokenHandler().WriteToken(secToken);
        //get the expiration times and pass them to the client
        var expires_sec = Convert.ToInt32(_configuration["JwtSettings:ExpirationTimeInMinutes"]) * 60;
        return Ok(new LoginToken()
        {
            access_token = jwt,
            user_id = user.Id.ToString(),
            expires_in = expires_sec,
            token_type = "Bearer"
        });
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Login Error");
        return StatusCode(500);
    }
}

5. Setup Program.cs

In the end, we also need to add the service to program.cs file. Before that, we need to install the below package to API project from Nuget first

Microsoft.AspNetCore.Authentication.JwtBearer

and add the below codes

// Add Authentication services & middlewares
builder.Services.AddAuthentication(opt =>
{
    opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        RequireExpirationTime = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
        ValidAudience = builder.Configuration["JwtSettings:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.
                GetBytes(builder.Configuration["JwtSettings:SecurityKey"]))
    };
});

builder.Services.AddScoped<JwtService>();

Ok, let’s take a look at the result in the swagger UI

Great! We got the token after login successfully!

6. Authorize the User Controller

We have created the login token but we still need to add the authorization to the user controller, otherwise, the token will be useless, just adding the attribute to the user controller will be ok

[Authorize()]
public class UserController : ControllerBase
{
    //...
}

7. Conclusion

We have learned how to generate and handle the Jwt on the server side (API), the main port that uses the JwtSecurityToken to generate a token and serialize it into a JWT format and return it to the client. Because that’s more complex for implementing on the client side, so I split to part 2 to describe how to handle it on the client side in the next article! 🙂

Loading

Views: 11
Total Views: 451 ,

One thought on “How to Implement JWT in Core API and Angular – Part 1

Leave a Reply

Your email address will not be published. Required fields are marked *