Client Validation using JWT Authentication in ASP.NET Core Web API

Client Validation using JWT Authentication in ASP.NET Core Web API

In this article, I will discuss how to Implement Client Validation using JWT Authentication in ASP.NET Core Web API Applications. Please read our previous article discussing Role-Based JWT Authentication in ASP.NET Core Web API.

Define Client Model

First, create a model to represent the client information. This model will include the client’s identifier, secret key, and any other relevant details such as Issuer or Audience. So, create a class file named ApiClient.cs within the Models folder of the JWTAuthServer application and copy and paste the following code:

namespace JWTAuthServer.Models
{
    public class ApiClient
    {
        //A unique identifier for the client.
        //This is used to reference the client in authentication processes
        public string ClientId { get; set; }

        //It is used for signing JWTs.
        //This key is important for security as it ensures that tokens issued by the server can be verified as authentic and unaltered.
        public string SecretKey { get; set; }

        //The URL of the server that issues the JWT.
        //This is used in the JWT's "iss" field and helps token recipients verify the origin of the token.
        public string Issuer { get; set; }

        // Intended to specify the recipients of the JWT, typically identified by a URL.
        // The audience value is used in the JWT's "aud" field and is essential for ensuring that a JWT is presented to the correct party.
        public string Audience { get; set; }
    }
}
Register Multiple Clients

Whenever a new client registers with our application, we need to store the client details in the database. But, for simplicity, let us create a list to store all clients of our application. So, create a class file named ClientStore.cs within the Models folder of our JWTAuthServer application and then copy and paste the following code. The Clients list is statically initialized with three clients, each configured with a specific ClientId, SecretKey, Issuer, and Audience.

namespace JWTAuthServer.Models
{
    public static class ClientStore
    {
        public static List<ApiClient> Clients = new List<ApiClient>
        {
            //You need to replace the values as per your requirements
            new ApiClient { ClientId = "Client1", SecretKey = "KHPK6Ucf/zjvU4qW8/vkuuGLHeIo0l9ACJiTaAPLKbk=", Issuer = "https://localhost:7035", Audience = "http://localhost:5001" },
            new ApiClient { ClientId = "Client2", SecretKey = "240602OyHP2B9Zr+GuY24Q9Tv0Ow5szxNGJKsko0rfU=", Issuer = "https://localhost:7035", Audience = "http://localhost:5002" },
            new ApiClient { ClientId = "Client3", SecretKey = "kHgf9wc7CcuLJ3zDFahfahcTyD9ySJ50zEb1vLT+YLs=", Issuer = "https://localhost:7035", Audience = "http://localhost:5003" }
        };
    }
}

Here, the Issuer is the URL of the Authentication Server Base Address. Each client has its own SecretKey, which is a good security practice that allows distinct verification processes for tokens issued to different clients.

Generating a Unique Secret Key for Each Client:

The secret keys provided for each client should be securely generated and protected. Please create a Dot Net Console application and then modify the Program class as follows to generate unique Secret Keys for each client. The following code is self-explained, so please read the comment lines for a better understanding.

using System.Security.Cryptography;
namespace KeyGeneratorApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // Allocate an array for storing random bytes.
            // This array has 32 bytes, equivalent to 256 bits.
            var randomBytes = new byte[32];

            // Create an instance of a cryptographic random number generator.
            using (var rng = RandomNumberGenerator.Create())
            {
                // Fill the previously defined array with securely generated random bytes.
                rng.GetBytes(randomBytes);
            } // The 'using' block ensures that the RandomNumberGenerator object is properly disposed of after use.

            // Convert the random bytes array into a Base64 string to make it readable and easy to handle as text.
            Console.WriteLine(Convert.ToBase64String(randomBytes));

            // Pause the program and wait for the user to press a key so they can see the output before the console window closes.
            Console.ReadKey();
        }
    }
}

Now, run the above code for each client and get the Secret key.

Modifying the Auth Controller:

Next, we need to modify the Auth Controller of our JWTAuthServer application, which generates the Token and Refresh tokens. Create a controller that will authenticate clients and users and issue JWTs. So, modify the AuthController as follows:

using JWTAuthServer.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace JWTAuthServer.Controllers
{
[Route("api/[controller]")]  
[ApiController]  
public class AuthController : ControllerBase
{
private readonly TokenService _tokenService;  
public AuthController(TokenService tokenService)
{
_tokenService = tokenService;  // Initialize token service.
}
// Action method to handle login requests. It expects a Login model in the request body.
[HttpPost("Login")]
public async Task<IActionResult> Login([FromBody] Login request)
{
// Validate the model state (input validation).
if (!ModelState.IsValid)
{
return BadRequest("Invalid request body.");  // Return bad request if the model state is invalid.
}
try
{
// Extract the client ID from the "Client-ID" request header.
var clientIdHeader = Request.Headers["Client-ID"].FirstOrDefault();
// Check if the client ID was provided in the request header
if (string.IsNullOrEmpty(clientIdHeader))
{
return Unauthorized("Client ID must be provided in the 'Client-ID' header.");
}
// Look for the client in the static ClientStore using the provided ClientId from the header.
var client = ClientStore.Clients.FirstOrDefault(c => c.ClientId == clientIdHeader);
// If no client is found, return an unauthorized response with a custom message.
if (client == null)
{
return Unauthorized("Invalid Client Credentials.");
}
// Attempt to find a user that matches the provided username and password.
var user = UserStore.Users.FirstOrDefault(u => u.Username == request.Username && u.Password == request.Password);
// Check if user was not found.
if (user == null)
{
return Unauthorized("Invalid user credentials.");  // Return unauthorized if no user is found.
}
// Issue a new access token for the user.
var accessToken = IssueAccessToken(user, client);
// Generate a new refresh token.
var refreshToken = GenerateRefreshToken();
// Save the new refresh token with associated user information.
await _tokenService.SaveRefreshToken(user.Username, refreshToken);
// Return the generated access and refresh tokens.
return Ok(new { AccessToken = accessToken, RefreshToken = refreshToken });
}
catch (Exception ex)
{
// Handle any exceptions that occur during the login process.
return StatusCode(500, $"Internal server error: {ex.Message}");  // Return a 500 internal server error on exception.
}
}
// Generates an access token for the specified user.
private string IssueAccessToken(User user, ApiClient client)
{
// Generate a secret key from the client's secret key.
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(client.SecretKey));
// Create signing credentials using the secret key and specifying the HMAC SHA256 algorithm.
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
// Prepare claims to be included in the token.
var claims = new List<Claim>
{
new Claim("Myapp_User_Id", user.Id.ToString()),  // Custom claim for user ID.
new Claim(ClaimTypes.NameIdentifier, user.Username),  // Standard claim for user identifier.
new Claim(ClaimTypes.Email, user.Email),  // Standard claim for user's email.
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),  // JWT standard claim for subject.
new Claim("Client-Id", client.ClientId)  // Custom claim for Cient.
};
// Add role claims for the user.
user.Roles.ForEach(role => claims.Add(new Claim(ClaimTypes.Role, role)));
// Create the JWT token using the claims, issuer, audience, etc., and set its expiration.
var token = new JwtSecurityToken(
issuer: client.Issuer,
audience: client.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(30),  // Set access token to expire in 30 minutes.
signingCredentials: credentials);
// Return the serialized token.
return new JwtSecurityTokenHandler().WriteToken(token);
}
// Generates a new refresh token using a cryptographic random number generator.
private string GenerateRefreshToken()
{
var randomNumber = new byte[32];  // Prepare a buffer to hold the random bytes.
using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);  // Fill the buffer with cryptographically strong random bytes.
return Convert.ToBase64String(randomNumber);  // Convert the bytes to a Base64 string and return.
}
}
// Refreshes an access token using a valid refresh token.
[HttpPost("RefreshToken")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequest request)
{
// Validate the refresh token request.
if (request == null || string.IsNullOrWhiteSpace(request.RefreshToken))
{
return BadRequest("Refresh token is required.");  // Return bad request if no refresh token is provided.
}
try
{
// Extract the client ID from the "Client-ID" request header.
var clientIdHeader = Request.Headers["Client-ID"].FirstOrDefault();
// Check if the client ID was provided in the request header
if (string.IsNullOrEmpty(clientIdHeader))
{
return Unauthorized("Client ID must be provided in the 'Client-ID' header.");
}
// Look for the client in the static ClientStore using the provided ClientId from the header.
var client = ClientStore.Clients.FirstOrDefault(c => c.ClientId == clientIdHeader);
// If no client is found, return an unauthorized response with a custom message.
if (client == null)
{
return Unauthorized("Invalid Client Credentials.");
}
// Retrieve the username associated with the provided refresh token.
var username = await _tokenService.RetrieveUsernameByRefreshToken(request.RefreshToken);
if (string.IsNullOrEmpty(username))
{
return Unauthorized("Invalid refresh token.");  // Return unauthorized if no username is found (invalid or expired token).
}
// Retrieve the user by username.
var user = UserStore.Users.FirstOrDefault(u => u.Username == username);
if (user == null)
{
return Unauthorized("Invalid user.");  // Return unauthorized if no user is found.
}
// Issue a new access token and refresh token for the user.
var accessToken = IssueAccessToken(user, client);
var newRefreshToken = GenerateRefreshToken();
// Save the new refresh token.
await _tokenService.SaveRefreshToken(user.Username, newRefreshToken);
// Return the new access and refresh tokens.
return Ok(new { AccessToken = accessToken, RefreshToken = newRefreshToken });
}
catch (Exception ex)
{
// Handle any exceptions during the refresh process.
return StatusCode(500, $"Internal server error: {ex.Message}");  // Return a 500 internal server error on exception.
}
}
// Revokes a refresh token to prevent its future use.
[HttpPost("RevokeToken")]
public async Task<IActionResult> RevokeToken([FromBody] RefreshTokenRequest request)
{
// Validate the revocation request.
if (request == null || string.IsNullOrWhiteSpace(request.RefreshToken))
{
return BadRequest("Refresh token is required.");  // Return bad request if no refresh token is provided.
}
try
{
// Extract the client ID from the "Client-ID" request header.
var clientIdHeader = Request.Headers["Client-ID"].FirstOrDefault();
// Check if the client ID was provided in the request header
if (string.IsNullOrEmpty(clientIdHeader))
{
return Unauthorized("Client ID must be provided in the 'Client-ID' header.");
}
// Look for the client in the static ClientStore using the provided ClientId from the header.
var client = ClientStore.Clients.FirstOrDefault(c => c.ClientId == clientIdHeader);
// If no client is found, return an unauthorized response with a custom message.
if (client == null)
{
return Unauthorized("Invalid Client Credentials.");
}
// Attempt to revoke the refresh token.
var result = await _tokenService.RevokeRefreshToken(request.RefreshToken);
if (!result)
{
return NotFound("Refresh token not found.");  // Return not found if the token does not exist.
}
return Ok("Token revoked.");  // Return success message if the token is successfully revoked.
}
catch (Exception ex)
{
// Handle any exceptions during the revocation process.
return StatusCode(500, $"Internal server error: {ex.Message}");  // Return a 500 internal server error on exception.
}
}
}
}
Creating ClientStore in Resource Server:

Next, we also need to fetch the Client data from the database. As we are hardcoding the client information, so we also need to create the client store same as the Authentication server. Create a class file named ClientStore.cs within the Models folder of the APIResourceServer application, and then copy and paste the following code.

namespace APIResourceServer.Models
{
public class ApiClient
{
public string ClientId { get; set; }
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
}
public class ClientStore
{
public static List<ApiClient> Clients = new List<ApiClient>
{
//You need to replace the values as per your requirements
new ApiClient { ClientId = "Client1", SecretKey = "KHPK6Ucf/zjvU4qW8/vkuuGLHeIo0l9ACJiTaAPLKbk=", Issuer = "https://localhost:7035", Audience = "http://localhost:5001" },
new ApiClient { ClientId = "Client2", SecretKey = "240602OyHP2B9Zr+GuY24Q9Tv0Ow5szxNGJKsko0rfU=", Issuer = "https://localhost:7035", Audience = "http://localhost:5002" },
new ApiClient { ClientId = "Client3", SecretKey = "kHgf9wc7CcuLJ3zDFahfahcTyD9ySJ50zEb1vLT+YLs=", Issuer = "https://localhost:7035", Audience = "http://localhost:5003" }
};
}
}
Modifying JWT Authentication Configuration in Resource Server:

Next, in the APIResourceServer application, we need to configure the authentication services to use JWT to validate the Client. So, please modify the Program.cs class file as follows:

using APIResourceServer.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
namespace APIResourceServer
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the DI container, enabling controllers and JSON options configuration.
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
// Configure JsonSerializer to not change property names of the models.
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
// Adding Swagger for API documentation and testing interface.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Middleware to manually extract and validate JWT from Authorization header.
app.Use(async (context, next) =>
{
// Extract the token from the Authorization header.
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
{
try
{
// Extract the client ID from the JWT token.
var clientId = GetClientIdFromToken(token);
// Find client configuration using the extracted client ID.
var clientConfig = ClientStore.Clients.FirstOrDefault(c => c.ClientId == clientId);
// Set up token validation parameters based on the client configuration.
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = clientConfig.Issuer,
ValidAudience = clientConfig.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(clientConfig.SecretKey))
};
// Validate the token and retrieve the principal (user identity).
var handler = new JwtSecurityTokenHandler();
var principal = handler.ValidateToken(token, validationParameters, out var validatedToken);
// Attach the user identity to the current HTTP context if validation is successful.
context.User = principal;
}
catch (SecurityTokenExpiredException)
{
// Handle expired token by returning a specific unauthorized response.
await ReturnUnauthorizedResponse(context, "Token is expired");
return;
}
catch (SecurityTokenValidationException)
{
// Handle invalid token by returning a specific unauthorized response.
await ReturnUnauthorizedResponse(context, "Token is invalid");
return;
}
catch (Exception ex)
{
// Handle other exceptions that might occur during JWT validation.
await ReturnUnauthorizedResponse(context, "Authorization failed: " + ex.Message);
return;
}
}
// Proceed to the next middleware in the pipeline.
await next();
});
// Configure the HTTP request pipeline for development environment.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// Redirect HTTP requests to HTTPS.
app.UseHttpsRedirection();
// Register authentication middleware to enforce authenticated access.
app.UseAuthentication();
// Register authorization middleware to enforce role-based and policy-based authorization.
app.UseAuthorization();
// Map routes to controllers.
app.MapControllers();
// Start handling requests.
app.Run();
}
// Method to construct and send an unauthorized response with a JSON-formatted error message.
private static async Task ReturnUnauthorizedResponse(HttpContext context, string message)
{
context.Response.StatusCode = 401; // HTTP status code for unauthorized.
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(new { error = message }));
}
// Method to extract the client ID from a JWT.
private static string GetClientIdFromToken(string token)
{
var jwtToken = new JwtSecurityToken(token);
// Extract the value of the 'Client-Id' claim.
return jwtToken.Claims.First(claim => claim.Type == "Client-Id").Value;
}
}
}
Modifying the Client Application:

Next, we need to include the Client-ID header in the request while calling the Token endpoints from our client application. So, modify the Client Application code as follows:

using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace JWTClientApp
{
public class Program
{
// Static HttpClient to be reused throughout the lifetime of the application.
private static readonly HttpClient httpClient = new HttpClient();
// Base URL for user-related API operations.
private static readonly string baseUrl = "https://localhost:7239/api/users";
// Main method, the entry point of the program.
static async Task Main(string[] args)
{
try
{
// Authenticate and get both access and refresh tokens.
var tokens = await AuthenticateAndGetToken();
Console.WriteLine("Access Token received: " + tokens.AccessToken);
Console.WriteLine("Refresh Token received: " + tokens.RefreshToken);
// Example CRUD operations using the received tokens.
Console.WriteLine(await GetUser(tokens.AccessToken, tokens.RefreshToken, 1)); // Get user with ID 1.
Console.WriteLine(await CreateUser(tokens.AccessToken, tokens.RefreshToken, new User { Id = 4, Username = "newuser", Email = "newuser@example.com" }));
Console.WriteLine(await UpdateUser(tokens.AccessToken, tokens.RefreshToken, 4, new User { Id = 4, Username = "updatedUser", Email = "updateduser@example.com" }));
Console.WriteLine(await DeleteUser(tokens.AccessToken, tokens.RefreshToken, 4)); // Delete user with ID 4.
Console.ReadKey(); // Pause the console so the user can read the output.
}
catch (Exception ex)
{
// Catch any exceptions and display the error message.
Console.WriteLine("Error: " + ex.Message);
}
}
// Authenticates with the API to retrieve access and refresh tokens.
static async Task<(string AccessToken, string RefreshToken)> AuthenticateAndGetToken()
{
var loginUrl = "https://localhost:7035/api/Auth/Login"; // URL for the login endpoint.
var loginRequestBody = new { Username = "test", Password = "password" }; // Request body with credentials.
var requestContent = new StringContent(JsonSerializer.Serialize(loginRequestBody), Encoding.UTF8, "application/json"); // Serialize the body to JSON.
var request = new HttpRequestMessage(HttpMethod.Post, loginUrl)
{
Content = requestContent
};
request.Headers.Add("Client-ID", "Client1");  // Add custom Client-ID header
// Send the login request.
var response = await httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync(); // Read the error message from the response.
throw new Exception($"Login failed: {errorContent}"); // Throw an exception if login fails.
}
var loginResponseContent = await response.Content.ReadAsStringAsync(); // Read the successful login response.
var tokenObject = JsonSerializer.Deserialize<JsonElement>(loginResponseContent); // Deserialize the JSON response to access the tokens.
var accessToken = tokenObject.GetProperty("AccessToken").GetString(); // Extract the access token.
var refreshToken = tokenObject.GetProperty("RefreshToken").GetString(); // Extract the refresh token.
return (AccessToken: accessToken, RefreshToken: refreshToken); // Return both tokens.
}
// Refreshes the access token using a refresh token.
static async Task<(string AccessToken, string RefreshToken)> RefreshAccessToken(string refreshToken)
{
var refreshUrl = "https://localhost:7035/api/Auth/RefreshToken"; // URL for the refresh token endpoint.
var refreshContent = new StringContent(JsonSerializer.Serialize(new { RefreshToken = refreshToken }), Encoding.UTF8, "application/json"); // Serialize the refresh token request to JSON.
var request = new HttpRequestMessage(HttpMethod.Post, refreshUrl)
{
Content = refreshContent
};
request.Headers.Add("Client-ID", "Client1");  // Add custom Client-ID header
// Send the refresh token request..
var response = await httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync(); // Read the error message from the response.
throw new Exception($"Refresh token failed: {errorContent}"); // Throw an exception if refresh fails.
}
var refreshResponseContent = await response.Content.ReadAsStringAsync(); // Read the successful refresh response.
var tokenObject = JsonSerializer.Deserialize<JsonElement>(refreshResponseContent); // Deserialize the JSON response to access the new tokens.
var accessToken = tokenObject.GetProperty("AccessToken").GetString(); // Extract the new access token.
var newRefreshToken = tokenObject.GetProperty("RefreshToken").GetString(); // Extract the new refresh token.
return (AccessToken: accessToken, RefreshToken: newRefreshToken); // Return the refreshed tokens.
}
static async Task<string> GetUser(string accessToken, string refreshToken, int userId)
{
var response = await SendAuthorizedRequest(HttpMethod.Get, $"{baseUrl}/{userId}", accessToken, null);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var tokens = await RefreshAccessToken(refreshToken);
//Update the Refresh and Access Token
refreshToken = tokens.RefreshToken;
accessToken = tokens.AccessToken;
response = await SendAuthorizedRequest(HttpMethod.Get, $"{baseUrl}/{userId}", accessToken, null);
}
return await ProcessResponse(response, "Get User");
}
static async Task<string> CreateUser(string accessToken, string refreshToken, User user)
{
//Testing the Refresh Token Manually
var tokens = await RefreshAccessToken(refreshToken);
refreshToken = tokens.RefreshToken;
accessToken = tokens.AccessToken;
var response = await SendAuthorizedRequest(HttpMethod.Post, baseUrl, accessToken, new StringContent(JsonSerializer.Serialize(user), Encoding.UTF8, "application/json"));
return await ProcessResponse(response, "Create User");
}
static async Task<string> UpdateUser(string accessToken, string refreshToken, int userId, User user)
{
var response = await SendAuthorizedRequest(HttpMethod.Put, $"{baseUrl}/{userId}", accessToken, new StringContent(JsonSerializer.Serialize(user), Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var tokens = await RefreshAccessToken(refreshToken);
//Update the Refresh and Access Token
refreshToken = tokens.RefreshToken;
accessToken = tokens.AccessToken;
response = await SendAuthorizedRequest(HttpMethod.Put, $"{baseUrl}/{userId}", accessToken, new StringContent(JsonSerializer.Serialize(user), Encoding.UTF8, "application/json"));
}
return await ProcessResponse(response, "Update User");
}
static async Task<string> DeleteUser(string accessToken, string refreshToken, int userId)
{
var response = await SendAuthorizedRequest(HttpMethod.Delete, $"{baseUrl}/{userId}", accessToken, null);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var tokens = await RefreshAccessToken(refreshToken);
//Update the Refresh and Access Token
refreshToken = tokens.RefreshToken;
accessToken = tokens.AccessToken;
response = await SendAuthorizedRequest(HttpMethod.Delete, $"{baseUrl}/{userId}", accessToken, null);
}
return await ProcessResponse(response, "Delete User");
}
// Generic method to send authorized HTTP requests and handle automatic token refresh if needed.
static async Task<HttpResponseMessage> SendAuthorizedRequest(HttpMethod method, string url, string token, HttpContent content)
{
var request = new HttpRequestMessage(method, url) // Prepare the HTTP request.
{
Content = content, // Set the request content if any.
Headers =
{
Authorization = new AuthenticationHeaderValue("Bearer", token) // Set the authorization header using the bearer token.
}
};
return await httpClient.SendAsync(request); // Send the request.
}
// Process the HTTP response, return a formatted string indicating success or type of failure.
static async Task<string> ProcessResponse(HttpResponseMessage response, string action)
{
var responseBody = await response.Content.ReadAsStringAsync(); // Read the response body as a string.
if (response.IsSuccessStatusCode)
{
return $"{action} succeeded: {responseBody}"; // Return success message with the response body.
}
switch (response.StatusCode)
{
case HttpStatusCode.Unauthorized:
return $"{action} Failed: Unauthorized - Token may be invalid or expired"; // Handle unauthorized responses.
case HttpStatusCode.Forbidden:
return $"{action} Failed: Forbidden - Insufficient permissions"; // Handle forbidden responses.
default:
return $"{action} Failed: {response.StatusCode} - {responseBody}"; // Handle other types of errors.
}
}
}
// Simple user model to demonstrate API data interactions.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
}
Output:

In the next article, I will discuss how to implement SSO (Single Sign-On) in ASP.NET Core Web API Application. In this article, I explain How to Implement Client Validation using JWT Token-Based Authentication in ASP.NET Core Web API Application with an Example. I hope you enjoy this How to Implement Client Validation using JWT Token-Based Authentication in ASP.NET Core Web API article.

Leave a Reply

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