Introduction
Authentication is one of the most important features in any application. Users need to register, login, and access resources securely.
ASP.NET Core Identity provides a ready-made solution for:
User Management
Password Hashing
Role Management
Claims Management
Authorization
Security Features
Project Structure
A simple project structure:
DotNetIdentity
│
├── Controllers
│ └── UserController.cs
│
├── Data
│ ├── TestDbContext.cs
│ └── Entity
│ └── ApplicationUser.cs
│
├── Model
│ ├── JWTSettings.cs
│ └── DTO
│ └── UserDto.cs
│
├── Program.cs
│
└── appsettings.json
Required NuGet Packages
Install the following packages.
Identity Package
Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore
Purpose:
IdentityUser
IdentityRole
UserManager
RoleManager
IdentityDbContext
Entity Framework Core SQL Server
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Purpose:
SQL Server connectivity
EF Core Migration Tools
Install-Package Microsoft.EntityFrameworkCore.Tools
Purpose:
Add-Migration
Update-Database
JWT Authentication Package
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Purpose:
JWT Validation
Bearer Authentication
Swagger Package
Install-Package Swashbuckle.AspNetCore
Purpose:
API Documentation
JWT Testing
Step 1: Create JWT Settings Model
File Location
Model/JWTSettings.cs
Code
namespace DotNetIdentity.Model
{
public class JWTSettings
{
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public string SecretKey { get; set; } = string.Empty;
public int ExpireInMinutes { get; set; }
}
}
Purpose
This class stores JWT configuration values from appsettings.json.
Step 2: Configure appsettings.json
File Location
appsettings.json
Code
{
"ConnectionStrings": {
"DefaultConnection": "Server=YourMSSqlServerName;Initial Catalog=TestDbIdentity;Integrated Security=true;TrustServerCertificate=true;"
},
"JWTSettings": {
"Issuer": "DemoApp",
"Audience": "UserApp",
"SecretKey": "#@493JDSSWOP()Y*&^&%^R$%R^&*@@#!@",
"ExpireInMinutes": 20
}
}
Purpose
Stores:
Database Connection String
JWT Secret Key
JWT Issuer
JWT Audience
Token Expiry Time
Step 3: Create Custom User
File Location
Data/Entity/ApplicationUser.cs
Code
using Microsoft.AspNetCore.Identity;
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
}
Why Create ApplicationUser?
IdentityUser already contains:
Id
Email
UserName
PasswordHash
PhoneNumber
ApplicationUser allows us to add extra fields like:
FirstName
LastName
Address
Step 4: Configure Identity in Program.cs
File Location
Program.cs
Identity Registration
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireDigit = false;
})
.AddEntityFrameworkStores<TestDbContext>();
Purpose
Registers:
UserManager
RoleManager
Identity Services
Configures password rules.
Step 5: Configure Database
Program.cs
builder.Services.AddDbContext<TestDbContext>(options =>
{
options.UseSqlServer(
builder.Configuration["ConnectionStrings:DefaultConnection"]);
});
Purpose
Connects Identity to the SQL Server database.
Step 6: Configure JWT Authentication
Program.cs
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme =
JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = settings.Issuer,
ValidateAudience = true,
ValidAudience = settings.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey =
new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
settings.SecretKey))
};
});
Purpose
Validates incoming JWT tokens.
Checks:
Issuer
Audience
Secret Key
Step 7: Configure Swagger Authentication
Program.cs
options.AddSecurityDefinition("Bearer",
new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT"
});
Purpose
Adds the Authorize button inside Swagger UI.
Step 8: Create User API
File Location
Controllers/UserController.cs
Create User Endpoint
[HttpPost("create-user")]
public async Task<IActionResult> CreateUser(UserDto userData)
{
var appUser = new ApplicationUser()
{
FirstName = userData.FirstName,
LastName = userData.LastName,
Email = userData.Email,
PhoneNumber = userData.Phone,
UserName = userData.Email
};
var result =
await _userManager.CreateAsync(
appUser,
userData.Password);
if (!result.Succeeded)
{
return BadRequest(result.Errors);
}
return Ok("User Created");
}
What Happens Here?
User sends data.
ApplicationUser object is created.
Identity hashes the password.
User gets stored in the
AspNetUserstable.
Step 9: Login API
Login Endpoint
[HttpPost("login-user")]
public async Task<IActionResult>
LoginUser(string email, string password)
{
var user =
await _userManager.FindByEmailAsync(email);
if (user == null)
return BadRequest();
var result =
await _userManager
.CheckPasswordAsync(user, password);
if (!result)
return BadRequest();
// Generate JWT
}
What Happens?
Email searched in the database.
Password checked against
PasswordHash.If valid, a JWT token is generated.
Step 10: Generate JWT Token
Login Method
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Email,email),
new Claim(ClaimTypes.Role,"admin") //hardcoded 'admin' role here
};
Create Token
var tokenObj = new JwtSecurityToken(
claims: claims,
issuer: jwtsettings.Issuer,
audience: jwtsettings.Audience,
expires: DateTime.UtcNow.AddMinutes(
jwtsettings.ExpireInMinutes),
signingCredentials:
new SigningCredentials(
new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
jwtsettings.SecretKey)),
SecurityAlgorithms.HmacSha256)
);
// Note: The token currently includes a hardcoded role ("admin"), so it will only authorize users as admin.
// In real-world applications, roles should not be hardcoded. Instead, they should be retrieved from the user's
// assigned roles in the database using Identity (e.g., _userManager.GetRolesAsync(user)).
Convert to String
var token =
new JwtSecurityTokenHandler()
.WriteToken(tokenObj);
Return Token
return Ok(new
{
token
});
Step 11: Protect APIs
Admin API
[Authorize(Roles = "admin")]
[HttpPost("test-auth-admin")]
public IActionResult TestAuthAdmin()
{
return Content(
"Authentication-admin Api Called");
}
Only users with the Admin role can access.
Manager API
[Authorize(Roles = "manager")]
[HttpPost("test-auth-manager")]
public IActionResult TestAuthManager()
{
return Content(
"Authentication-manager Api Called");
}
Only users with the Manager role are allowed.
Step 12: Middleware Pipeline
Program.cs
app.UseAuthentication();
app.UseAuthorization();
Order is important.
Authentication must come before Authorization.
Step 13: Create Database
Run Migration
Add-Migration InitialIdentity
Apply Migration
Update-Database
Identity automatically creates:
AspNetUsers
AspNetRoles
AspNetUserRoles
AspNetUserClaims
AspNetRoleClaims
AspNetUserTokens
AspNetUserLogins
Testing Flow
Register User
POST
/api/User/create-user
Creates a new user.
Login User
POST
/api/User/login-user
Returns:
{
"token": "eyJhbGc..."
}
Swagger Authorization
Click:
Authorize
Enter:
token here...
Now protected APIs can be called.
Conclusion
In this project we successfully implemented:
✅ ASP.NET Core Identity
✅ SQL Server Integration
✅ User Registration
✅ User Login
✅ JWT Authentication
✅ Role-Based Authorization
✅ Swagger JWT Integration
Identity handles password hashing and user management, while JWT provides secure stateless authentication for APIs. Together they form the foundation of modern ASP.NET Core authentication systems.
Join the conversation! Your thoughts help the community grow.