Motivation
JASON Web Token Authentication is not easy to implement, especially with all the features that come with any identity scheme such as roles, scopes, permissions, etc. For that, there are several third-party libraries that help provide all the needed services, one of which is IdentityServer provided by Duende Software. However, going with such as approach might be an overkill for your app, especially if you are not planning to benefit from all the features they offer. So, if all you need is a simple authentication scheme over the network, this article is for you.
Prerequisite
For this tutorial, we will need a working .NET project or more, as JWT authentication can cover multiple projects. I will be using .NET 6, but feel free to use any other version as long as you are able to maintain the version differences.
I will assume that you have multiple projects you want to apply authentication to. If not, the documentation will still work, but it will be simpler for you to implement.
I will be using MongoDB for this documentation, as I find it more needed than SQL databases, but feel free to another DB vendor as this is just a detail,
Server Project
Needed Packages
First, you need to add the needed dependencies to your project. For the server, there is only one needed package, and it is specific to the DB vendor you're using. Since I'm using MongoDB, the package to be added is AspNetCore.Identity.Mongo. If you're using MSSQL, then you'll need the packages Microsoft.AspNetCore.Identity.EntityFrameworkCore and Microsoft.AspNetCore.ApiAuthorization.IdentityServer which are provided by the Microsoft.
Setting Up Identity
In your Program.cs (or if you're using .NET prior to 6, then it's Startup.cs) class add the following code:
builder.Services.AddIdentityMongoDbProvider<User>(_ => { }, mongo =>
{
mongo.ConnectionString = "connectionString";
})
.AddDefaultTokenProviders();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidIssuer = "https://localhost",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret"))
};
});
Note: don't forget to change the issuer and the secret as you need. You can also apply additional configuration to the options, so feel free to check them up.
Also, you need to add the following lines before app.MapControllers()
app.UseAuthentication();
app.UseAuthorization();
If your server project is also your client, then you are set to go. However, if you will be consuming the access token in another project on more, then you need to apply the following to each on of them:
Client Project
Needed Package
You will need to add the package Microsoft.AspNetCore.Authentication.JwtBearer to your project dependencies.
Setting Up Identity
As we did in the server, we will be adding the following code to Program.cs
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidIssuer = "https://localhost",
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret"))
};
});
app.UseAuthentication();
app.UseAuthorization();
Protecting Controllers
The last step would be to specify which controllers or endpoints you want to protect by the access token. To do so, add the attribute [Authorize] over the whole class if you want to protect all your controller's endpoints, or above each one of the needed endpoints
[Authorize]
[ApiController]
[Route("[controller]")]
public class ArticleController : ControllerBase
{
...
}