This is post 1 of 1 in the series “How to Implement JWT”
In this series, I will show you how to implement JWT in .Net Core API and communicate with Angular.
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.
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.
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.
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;
}
}
We need to create an API login function to return the JWT to the client.
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; }
}
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);
}
}
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!
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. And we will discuss how to handle the JWT on the client side in the next article! 🙂
The post How to Implement JWT in Core API and Angular – Part 1 first appeared on Coder Blog.