Introduction
One thing that a web application, or any other application for that matter, cannot do without is user authentication, authorization, and account management. In this series, I am going to exemplify how you can use .NET Core Identity to leverage existing capabilities without reinventing the wheel. This will be a step-by-step series, and in the first part, I will show you how to configure a simple .NET Core web API to use this framework.
For this tutorial, I am going to use .NET 9 and PostgreSQL as the database. Additionally, I will demonstrate how to use Docker Compose to run the application, although the tutorial can also be completed without Docker. Without further ado, let's jump into it.
Step 1 - Creating a new project
Create a .NET core API - use the default weather app
. If you use Visual Studio
your app configuration should look something like this:
Step 2 - Adding proper tools and packages
Open a terminal at root of you project and run the following commands:
PostgreSQL package:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
AspNetCore.Identity package:
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
Entity Framework tools (needed for running the database migration):
dotnet tool install --global dotnet-ef
Entity Framework Design package (also needed for running the migration):
dotnet add package Microsoft.EntityFrameworkCore.Design
Step 3 - Custom UserEntity
In most applications using the default IdentityUser class provided by .NET core Identity is not enough. Therefore, in this tutorial, we are going to extend this class and use it to create our database context.
using Microsoft.AspNetCore.Identity;
namespace DemoBackend.Data
{
public class UserEntity : IdentityUser
{
//extend the IdentityUser with your custom additional properties
public int Age { get; set; }
}
}
Step 4 - Creating the database context
The database context is using the custom UserEntity
class defined at the previous step. Inside the OnModelCreating
method we are seeding the roles table with 2 roles (which we are going to use later in this series for exemplifying the authorization).
using DemoBackend.Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace DemoBackend.Data
{
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<UserEntity>(options)
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
//seeding data for roles - we will use these roles in the future
var adminRoleId = "d1b6a7e1-8c4b-4b8a-9b1d-1a2b3c4d5e6f";
var userRoleId = "e2c7b8f2-9d5c-4e8b-8a1d-2b3c4d5e6f7g";
builder.Entity<IdentityRole>().HasData(
new IdentityRole
{
Id = adminRoleId,
Name = "Admin",
NormalizedName = "ADMIN"
},
new IdentityRole
{
Id = userRoleId,
Name = "User",
NormalizedName = "USER"
}
);
}
}
}
Step 5 - Adding Identity configuration
Now that we have the database context, we need to configure the database connection and Identity settings. All these configurations are demonstrated in the following extension method:
using DemoBackend.Entities;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace DemoBackend.Extensions
{
public static class DbConnectionExtensions
{
public static void AddIdentityDbContext(this IServiceCollection services, WebApplicationBuilder builder)
{
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(connectionString));
//this service is required by the DataProtectorTokenProvider used in ASP.NET Core Identity
services.AddDataProtection();
//Identity configuration
services.AddIdentityCore<UserEntity>(options =>
{
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 4;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddTokenProvider<DataProtectorTokenProvider<UserEntity>>(TokenOptions.DefaultProvider);
//this is required to be able to use the UserManager service with the UserEntity
services.TryAddScoped<UserManager<UserEntity>>();
}
}
}
Step 6 - Running the project
You can either run the project using Docker Compose
(in which case you need a properly configured Dockerfile), or you can simply run it as a normal web API (in this case, you will need PostgreSQL installed on your local machine). I prefer running it with Docker Compose
because it eliminates the need to install any dependencies on my local machine and avoids potential installation issues.
Resources needed to run with docker compose
Assure that you have Docker installed
Dockerfile
- if you created the project using the configuration from step 1 you should already have itdocker-compose.yaml
(placed at same level as youDockerfile
) - please assure that theDockerfile
exposes the proper port (8080, in this case).
version: '3.9'
services:
webapi:
image: demo-backend
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
depends_on:
- db
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Host=db;Database=postgres;Username=postgres;Password=test
db:
image: postgres:latest
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: test
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Resources needed to run without docker
PotgresSQL installed locally
Connection string inside the
appsettings.json
file
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=postgres;Username=postgres;Password=test"
}
//other configurations
}
Step 7 - Add the proper configuration to your Program.cs
You should add the AddIdentityDbContext
extension method defined above. Additionally, you can include the migration logic to run at application startup. This will automatically apply any pending migrations without requiring manual execution. Your Program.cs
file should look like this:
using DemoBackend;
using DemoBackend.Extensions;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
//extension method defined at step 4
builder.Services.AddIdentityDbContext(builder);
var app = builder.Build();
// Apply migrations on startup
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
if (context.Database.GetPendingMigrations().Any())
{
context.Database.Migrate();
}
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 8 - Creating the migration
Now that you have everything setup, open a terminal at the root of your project and run the following command:
dotnet ef migrations add InitialMigration
This will create the Initial migration with the default Identity tables.
To apply this migration to your database, you can either run the project if you added the migration logic in your Program.cs
file, or you can run it manually using the following command:
dotnet ef database update
Step 9 - Running the project
To run you project using docker compose, open a terminal at the root of you API and run the following command: docker compose up --build
This should start both your database instance and your API. If you added the migration logic to the Program.cs
, it will also automatically apply the migration to the database specified in the Docker Compose webapi
environment configuration.
Step 10 - Check if the Identity tables have been created properly
If everything is successful, you should have the tables created as shown below. For more information about these tables check this official documentation.
Conclusions
In this tutorial, we have learned how to set up .NET Core Identity for a .NET Core web API using PostgreSQL. In the next session, we will build on this tutorial by implementing token-based authentication.
Top comments (0)