Use token authentication and Identity API endpoints in ASP.NET Core
If you are following previous articles of this series available here and here, you are probably comfortable working with Web API and class library projects in VS Code. We will take our example a step further and add authentication to the Web API.
Adding authentication to Web API is a bit tedious process because you are not looking for traditional cookie based authentication schemes that you use with MVC or Razor Pages. For protecting your Web API you typically resort to a token based schemes such as JSON Web Tokens (JWTs). In the past implementing a token based authentication schemes for ASP.NET Core Web API and Minimal API involved a lot of work. Luckily, a simple solution is available in .NET 8 Identity framework.
In the remainder of this article we will integrate ASP.NET Core Identity's token authentication with the Employees Web API we created in the earlier parts. So, make sure that you read and complete Part 1 and Part 2 of this series before going any further.
From the previous parts we have two projects -- a Web API project and a class library project -- ready in our solution. To store ASP.NET Core Identity related classes we will add another class library project to the solution called SecurityClassLibrary. Your VS Code Solution Explorer should look like this after adding the third project :
The SecurityClassLibrary also requires the following NuGet packages :
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Tools
So, add them using NuGet Gallery extension. The SecurityClassLibrary project after adding these packages is shown below:
We need to classes in this project namely AppIdentityDbContext and AppIdentityUser. The AppIdentityDbContext class represents a custom DbContext for communicating with the database storing user details and the AppIdentityUser class represents a user of the application. In our example we will use the application database to store the user details also but you can use a separate database if you want.
The AppIdentityUser class is shown below:
namespace SecurityClassLibrary;
public class AppIdentityUser: IdentityUser
{
}
As you can see, the AppIdentityUser class inherits from the IdentityUser base class from Microsoft.AspNetCore.Identity namespace. The base class already contains certain properties such as UserName, Email and Password. Any other application specific custom properties can be added to the AppIdentityUser class. In our example we don't want any custom properties and hence our AppIdentityUser is empty.
Then we create AppIdentityDbContext as shown below:
public class AppIdentityDbContext :
IdentityDbContext<AppIdentityUser>
{
const string connectionString = @"Data Source=.;
Initial Catalog=CodeDb;
User ID=*****;Password=*****;
encrypt=false";
public AppIdentityDbContext() : base()
{
}
protected override void OnConfiguring
(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(connectionString);
}
public AppIdentityDbContext
(DbContextOptions<AppIdentityDbContext> options)
: base(options)
{
}
}
This code should look quite similar to AppDbContext that you created in the EmployeeClassLibrary. The only difference is that it inherits from IdentityDbContext. And while inheriting you specify AppIdentityUser as the application user's type. Also notice that there is no DbSet in this class.
This completes SecurityClassLibrary project. Save everything and build the project.
Now, we will create and apply database migrations (similar to how we did for the EmployeeClassLibrary project) so that tables required to store user details get created in the database.
Open VS Code terminal window using View | terminal menu option and issue the following two commands one-byone.
dotnet ef migrations add Initial
dotnet ef database update
After successfully applying the migrations the following tables get added to the application database.
Now that the user tables have been created, you can comment out the connection string and the first constructor from AppIdentityDbContext (see earlier part for the explanation).
Next, we will move to the EmployeeWebApi project and wire ASP.NET Core Identity in Employee API.
Add a project reference to SecurityClassLibrary project so that we can access AppIdentityUser and AppIdentityDbContext classes in the EmployeeWebApi project.
Open Program.cs of the EmployeeWebApi project and register AppIdentityDbContext like this:
// add this line immediately after registering AppDbContext
builder.Services.AddDbContext
<AppIdentityDbContext>
(options => options.UseSqlServer(connStr));
Then add Identity API endpoints to the app :
builder.Services
.AddIdentityApiEndpoints<AppIdentityUser>()
.AddEntityFrameworkStores<AppIdentityDbContext>();
The AddIdentityApiEndpoints() and AddEntityFrameworkStores() methods specify the custom IdentityUser and IdentityDbContext classes created in the SecurityClassLibrary project.
Also add authorization services like this:
builder.Services.AddAuthorization();
And after builder.Build() add this line:
app.UseAuthorization();
Just before app.Run() line, add this code:
app.MapIdentityApi<AppIdentityUser>();
For your quick check, the complete Program.cs is given below:
using EmployeeClassLibrary;
using SecurityClassLibrary;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.BearerToken;
var builder = WebApplication.CreateBuilder(args);
var connStr = builder.Configuration.
GetConnectionString("AppDb");
builder.Services.AddDbContext
<AppDbContext>(options =>
options.UseSqlServer(connStr));
builder.Services.AddDbContext
<AppIdentityDbContext>
(options =>
options.UseSqlServer(connStr));
builder.Services.AddScoped
<IEmployeeRepository, EmployeeRepository>();
builder.Services
.AddIdentityApiEndpoints<AppIdentityUser>()
.AddEntityFrameworkStores<AppIdentityDbContext>();
builder.Services.AddAuthorization();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapIdentityApi<AppIdentityUser>();
app.Run();
At this stage we have wired Identity into the system but our Employee Web API is not yet protected. So, open EmployeesController.cs file and add the following:
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
// other code
}
As you can see, the EmployeesController is now decorated with [Authorize] attribute. This way, invoking any action of the Employees Web API will require valid security token.
If you run the EmployeesWebApi project, you will magically see the following API endpoints appearing in the Swagger UI.
So far so good. Our Web API is now protected. But how do we test the token generation and the overall security flow? For that we need to add some Swagger configuration so that we can use Swagger UI to generate and use the security token. We will do that in the next part of this series.
That's it for now! Keep coding!!