Implementation of JWT Authentication in ASP.NET Core
This is very important to secure the minimal API’s endpoints. The purpose of this article is to explain how you can easily secure your APIs using JSON web tokens for authentication and authorization.
ASP.NET Core 6 is a very simplified hosting model that allows us to build lightweight APIs.
First, you will have to create a new project using Visual Studio 2022 IDE.
--> Open Visual Studio
--> Create New Project
--> Select “ASP.Net Core Web API” from the list of templates displayed
--> Click Next
--> Specify Project Name and Location
--> Click Next
--> In Additional Information since we are using minimal API uncheck the checkbox that says “Use Controllers” and leaves “Authentication Type” as None (default)
--> Ensure these checkboxes are unchecked “Enable Docker”, “Configure for HTTP” and “Enable Open API Support” as we are not using any of these features here
--> Click Create
Next Step is to Create a HTTP Get endpoint in ASP.NET Core 6.
After creating a new application with minimal web api project few default lines will be added to your code in Program.cs replace this code with the following code snippet in order to keep things simple.
var builder = WebApplication.CreateBuilder(args)
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/jwt-security/get",() => "Hello JWT Web Security!").RequireAuthorization();
app.Run();;
Note: In the above code RequireAuthorization Extension method helps protecting your routes using authorization policies and force you to provide authentication information. The authorization middleware will use this information to validate the request for the current execution context.
If you execute this endpoint without this information, you’ll encounter a HTTP 401 Unauthorized error.
Now Install the JwtBearer NuGet package from
--> Go to Tools
-->Select NuGet Package Manager
-->Select Package Manager Console
-->Enter this Command “Install-Package Microsoft.AspNetCore.Authentication.JwtBearer”
-->Hit Enter
Specify a secret key in the appsettings.json file
--> Paste following object in your appsettings.json file
Recommended by LinkedIn
"JwtAuth": {
"SecretKey": "Sample Secret Key – For non-production learning purpose only"
}
Specify authentication settings in the Program.cs file
This “AddAuthentication” method is used to configure JWT authentication at the time when the application starts. It specifies the authentication scheme as JwtBearer. Also the call to the AddJwtBearer method helps configure token parameters.
The Issuer, Audience, and Key values are read from the appsettings.json config file. The TokenValidationParameters instance is used to indicate if the Issuer, Audience, Key, and Lifetime information should be validated or not.
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme =JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.RequireHttpsMetadata = false;
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(builder.Configuration["JwtAuth:SecretKey"])),
ValidateIssuer = false,
ValidateAudience = false,
};
});
Also include the following code snippet these lines will help to add authorization services to your application.
builder.Services.AddAuthorization();
Your Program.cs should also include the following methods to enable authentication and authorization capabilities.
app.UseAuthentication();
app.UseAuthorization();
Now Create a user model class
-->Right click on your solution
-->Select Add
-->Select New Item
-->Add Class Named “UserModel.cs”
-->Add these properties
public string UserName { get; set; }
public string UserPassword { get; set; }
This class will store the login credentials of users.
Create an endpoint to generate JSON Web Tokens
At last, we need to write the necessary code to generate and validate the JWTs we’ll use to authorize calls to the API. Once a token is generated in response to an initial request to the API, you can copy it and use it for authorization in all subsequent requests.
app.MapPost("/security/createToken",
[AllowAnonymous] (UsersModel user) =>
{
if (user.UserName == "Sohaib" && user.UserPassword == "Sohaib123")
{
var key = Encoding.ASCII.GetBytes
(builder.Configuration["JwtAuth:SecretKey"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim("Id", Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti,
Guid.NewGuid().ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(5),
SigningCredentials = new SigningCredentials
(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha512Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var jwtToken = tokenHandler.WriteToken(token);
var stringToken = tokenHandler.WriteToken(token);
return Results.Ok(stringToken);
}
return Results.Unauthorized();
});
Here is the complete source code of the program.cs file.
using JWTAuthenticationMinimalAPIs;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.RequireHttpsMetadata = false;
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(builder.Configuration["JwtAuth:SecretKey"])),
ValidateIssuer = false,
ValidateAudience = false,
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/jwt-security/get",() => "Hello JWT Web Security!").RequireAuthorization();
app.MapPost("/security/createToken",
[AllowAnonymous] (UsersModel user) =>
{
if (user.UserName == "Sohaib" && user.UserPassword == "Sohaib123")
{
var key = Encoding.ASCII.GetBytes
(builder.Configuration["JwtAuth:SecretKey"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim("Id", Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti,
Guid.NewGuid().ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(5),
SigningCredentials = new SigningCredentials\
(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha512Signature)
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var jwtToken = tokenHandler.WriteToken(token);\
var stringToken = tokenHandler.WriteToken(token);\
return Results.Ok(stringToken);
}
return Results.Unauthorized();
});
app.UseAuthentication();
app.UseAuthorization();
app.Run();
JWT authentication in action
We’ve passed the user credentials, i.e., the user name and password, in the body of the request.
Now, call the HTTP Get endpoint we created earlier and pass the generated token as a bearer token in the request header. If your generated token is valid, you’ll see the message.
very informative