In this article, we will implement an ASP.NET Core web API application using JWT for authentication and authorization. This application will handle login, logout, token refresh, and impersonation.
JWT Workflow
JWT Workflow
User Login: The user sends credentials to the website to log in.
Token Generation: The website validates the credentials, generates a JWT and a refresh token, and returns them to the user.
Subsequent Requests: The user sends the JWT with each request to access protected resources.
Token Verification: The website validates the JWT and processes the request if the token is valid.
Token Refresh: When the JWT is about to expire, the client sends the refresh token to get a new JWT and refresh token.
JWT Authentication Configuration
JwtTokenConfig Class
Configuration in Startup.cs
Install Microsoft.AspNetCore.Authentication.JwtBearer NuGet package
Add JWT Bearer authentication in the ConfigureServices method.
Add the app.UseAuthentication() method in the Configure method.
Token Generation and Login
JwtAuthManager Class
AccountController
Logout
Refresh the JWT Access Token
Refresh Token API Endpoint
Refresh Method in JwtAuthManager
public JwtAuthResult Refresh(string refreshToken, string accessToken, DateTime now)
{
var (principal, jwtToken) = DecodeJwtToken(accessToken);
if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256Signature))
{
throw new SecurityTokenException("Invalid token");
}
var userName = principal.Identity.Name;
if (!_usersRefreshTokens.TryGetValue(refreshToken, out var existingRefreshToken))
{
throw new SecurityTokenException("Invalid token");
}
if (existingRefreshToken.UserName != userName || existingRefreshToken.ExpireAt < now)
{
throw new SecurityTokenException("Invalid token");
}
return GenerateTokens(userName, principal.Claims.ToArray(), now); // need to recover the original claims
}
public (ClaimsPrincipal, JwtSecurityToken) DecodeJwtToken(string token)
{
if (string.IsNullOrWhiteSpace(token))
{
throw new SecurityTokenException("Invalid token");
}
var principal = new JwtSecurityTokenHandler()
.ValidateToken(token,
new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = _jwtTokenConfig.Issuer,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(_secret),
ValidAudience = _jwtTokenConfig.Audience,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
},
out var validatedToken);
return (principal, validatedToken as JwtSecurityToken);
}
In the code above, the DecodeJwtToken method validates the original JWT access token and recovers the original claims. The Refresh method generates a new access token and refresh token if the provided refresh token is valid.
Summary
In the code above, the DecodeJwtToken method validates the original JWT access token and recovers the original claims. The Refresh method generates a new access token and refresh token if the provided refresh token is valid.