Authentication using ASP.NET Core Identity (.NET 10)
Add authentication to your .NET 10 Web API in minutes using the built-in Identity API endpoints - no custom controllers needed.
Connect with me:
🚀 Sponsored
Struggling with slow EF Core operations? With ZZZ Projects’ EF Core Extensions, you can boost performance like never before. Experience up to 14× faster Bulk Insert, Update, Delete, and Merge — and cut your save time by as much as 94%.
Introduction
If you’ve ever built a .NET Web API that needed authentication, you know the drill: create an AuthController, wire up UserManager, write register/login endpoints from scratch...
It’s a lot of boilerplate.
Not anymore.
Starting with .NET 8 and improved in .NET 10, ASP.NET Core ships with Identity API Endpoints - a set of ready-made endpoints for registration, login, token refresh, and more, all backed by ASP.NET Core Identity and Bearer tokens.
In this article, I’ll walk you through setting up authentication from zero in a .NET 10 Minimal API project:
Installing and configuring Identity + EF Core
Running database migrations
Using the built-in Identity endpoints
Protecting routes with
RequireAuthorization()Authenticating with Bearer tokens
🎬 Watch the full video here:
What are Identity API Endpoints?
MapIdentityApi<TUser>() is a single method call that maps all the authentication endpoints you need:
/register - POST - Register a new user
/login - POST - Login and receive a Bearer token
/refresh - POST - Refresh an expired token
/confirmEmail - GET - Confirm email address
/manage/info - GET / POST - Get or update user infoNo controllers. Just one line.
Project Setup
1. Create the Project
dotnet new webapi -n IdentityApiDemo --use-minimal-apis
cd IdentityApiDemoMake sure you’re targeting .NET 10 in your .csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>2. Install Required Packages
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.ToolsOr if you prefer SQLite for local development:
dotnet add package Microsoft.EntityFrameworkCore.SqliteThe Data Model
AppDbContext — Wiring Up Identity
csharp
public class AppDbContext : IdentityDbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
}Key point: Inherit from IdentityDbContext instead of plain DbContext. This automatically includes all the Identity tables (AspNetUsers, AspNetRoles, AspNetUserTokens, etc.) — no extra configuration needed.
Program.cs Setup
Here’s the full Program.cs:
using IdentityApiDemo.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// 1. Add OpenAPI
builder.Services.AddOpenApi();
// 2. Configure DbContext
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
// 3. Configure Identity with Bearer token authentication
builder.Services
.AddIdentityApiEndpoints<IdentityUser>()
.AddEntityFrameworkStores<AppDbContext>();
// 4. Add Authorization
builder.Services.AddAuthorization();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.MapScalarApiReference();
}
app.UseHttpsRedirection();
// 5. Map the built-in Identity endpoints
app.MapIdentityApi<IdentityUser>();
// 6. Your protected endpoints
app.MapGet("/me", (ClaimsPrincipal user) =>
{
return Results.Ok(new
{
Email = user.FindFirstValue(ClaimTypes.Email),
Id = user.FindFirstValue(ClaimTypes.NameIdentifier)
});
})
.RequireAuthorization();
app.MapGet("/public", () => Results.Ok("Anyone can see this!"));
await app.RunAsync();What’s happening here?
AddIdentityApiEndpoints<IdentityUser>()— registers Identity services and configures Bearer token authentication in one call. No need to manually callAddAuthentication().AddBearerToken().AddEntityFrameworkStores<AppDbContext>()— tells Identity to use your EF Core context for persistence.MapIdentityApi<IdentityUser>()— maps all the/register,/login,/refresh, etc. routes.RequireAuthorization()— protects an endpoint; returns401 Unauthorizedif no valid token is provided.
Database Migration
1. Add the Initial Migration
dotnet ef migrations add InitialIdentitySchemaThis generates a migration that creates all the Identity tables:
AspNetUsers— your user accountsAspNetRoles— role definitionsAspNetUserRoles— user-role assignmentsAspNetUserClaims— user claimsAspNetUserTokens— tokens (refresh tokens live here)AspNetUserLogins— external login providers
2. Apply the Migration
dotnet ef database updateOr apply it programmatically on startup:
// In Program.cs, before app.Run()
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync();
}Important: The automatic migration approach is convenient in development but should be handled carefully in production. Consider using a deployment pipeline to run migrations separately.
Using the Identity Endpoints
Register a New User
POST /register
Content-Type: application/json
{
"email": "john@example.com",
"password": "SecurePass123!"
}Response: 200 OK (empty body on success)
Login and Get a Bearer Token
POST /login
Content-Type: application/json
{
"email": "john@example.com",
"password": "SecurePass123!"
}Response:
{
"tokenType": "Bearer",
"accessToken": "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600,
"refreshToken": "CfDJ8NzFy..."
}The accessToken is a standard Bearer token. Use it in subsequent requests.
Call a Protected Endpoint
GET /me
Authorization: Bearer eyJhbGciOiJodHRwOi8vd3d3...Response:
{
"email": "john@example.com",
"id": "a3f4b2c1-..."
}Without the token (or with an expired one):
401 UnauthorizedRefresh an Expired Token
POST /refresh
Content-Type: application/json
{
"refreshToken": "CfDJ8NzFy..."
}Response:
{
"tokenType": "Bearer",
"accessToken": "eyJhbGciOi...",
"expiresIn": 3600,
"refreshToken": "CfDJ8Abc..."
}The old refresh token is invalidated and a new one is issued.
Protecting Endpoints with RequireAuthorization()
Basic Protection
// Must be authenticated (any valid token)
app.MapGet("/dashboard", () => "Your dashboard")
.RequireAuthorization();Group-Level Authorization
Apply authorization to a whole group of routes at once instead of repeating .RequireAuthorization() on every endpoint:
var api = app.MapGroup("/api")
.RequireAuthorization();
api.MapGet("/orders", () => "All orders");
api.MapGet("/products", () => "All products");
api.MapPost("/orders", () => "Create order");
// All three require authenticationAllow Anonymous on Specific Endpoints
If you protect a group but need to open up certain routes:
api.MapGet("/health", () => "OK")
.AllowAnonymous();Best Practices
1. Always Use HTTPS in Production
Bearer tokens are credentials - never send them over plain HTTP:
app.UseHttpsRedirection();2. Don’t Expose Sensitive Claims
// ✅ Only return what the client needs
app.MapGet("/me", (ClaimsPrincipal user) =>
{
return Results.Ok(new
{
Email = user.FindFirstValue(ClaimTypes.Email)
});
})
.RequireAuthorization();Full Flow Recap
1. POST /register → Create account
2. POST /login → Get accessToken + refreshToken
3. GET /me → Pass Bearer token in Authorization header
4. POST /refresh → Get a new accessToken when expiredKey Takeaways
AddIdentityApiEndpoints<IdentityUser>()— wires up Identity + Bearer auth in one callMapIdentityApi<IdentityUser>()— maps/register,/login,/refresh, and more automaticallyIdentityDbContext— inherit from it to get all Identity tables for freeMigrations — run
dotnet ef migrations add+dotnet ef database updateRequireAuthorization()— protects individual endpoints or entire route groupsBearer tokens — pass as
Authorization: Bearer <token>header; refresh with/refresh
You went from zero to authenticated API in minutes
Resources
Connect with me:
