Setting Up Identity-Based Authentication
1. Install Required NuGet Packages
Begin by adding the Identity framework package to your project:
Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
2. Define Custom Entity Models
Create domain entities that extend Identity's base classes, using long as the primary key type for better performance and clarity:
public class User : IdentityUser<long>
{
public string FullName { get; set; }
public string Academy { get; set; }
public string Major { get; set; }
public string Class { get; set; }
public string Department { get; set; }
public DateTime CreationTime { get; set; }
}
public class Role : IdentityRole<long>
{
public DateTime CreationTime { get; set; }
}
public class UserRole : IdentityUserRole<long>
{
}
3. Configure Database Context
Set up the database context with proper table mappings and seed initial data:
public class AppDbContext : IdentityDbContext<User, Role, long>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>().ToTable("Users");
builder.Entity<Role>().ToTable("Roles");
builder.Entity<UserRole>().ToTable("UserRoles");
// Relationships
builder.Entity<User>().HasMany(ur => ur.UserRoles).WithOne().HasForeignKey(ur => ur.UserId).OnDelete(DeleteBehavior.Cascade);
builder.Entity<Role>().HasMany(ur => ur.UserRoles).WithOne().HasForeignKey(ur => ur.RoleId).OnDelete(DeleteBehavior.Cascade);
// Seed roles
builder.Entity<Role>().HasData(new Role
{
Id = 1,
Name = "Admin",
NormalizedName = "ADMIN",
CreationTime = DateTime.UtcNow
});
// Seed admin user
var hasher = new PasswordHasher<User>();
var adminUser = new User
{
Id = 1,
UserName = "admin",
NormalizedUserName = "ADMIN",
FullName = "System Administrator",
Academy = "College of AI & Big Data",
Major = "Computer Science",
Class = "Class 11",
PhoneNumber = "1234567890",
Email = "admin@example.com",
NormalizedEmail = "ADMIN@EXAMPLE.COM",
EmailConfirmed = true,
TwoFactorEnabled = false,
SecurityStamp = Guid.NewGuid().ToString(),
CreationTime = DateTime.UtcNow
};
adminUser.PasswordHash = hasher.HashPassword(adminUser, "securePassword123");
builder.Entity<User>().HasData(adminUser);
// Assign role to user
builder.Entity<UserRole>().HasData(new UserRole
{
UserId = 1,
RoleId = 1
});
}
}
Apply migrations via CLI:
Add-Migration InitialCreate
Update-Database
4. Create DTOs for API Communication
Define lightweight transfer objects to decouple business logic from presentation layer:
public class UserDto
{
public long Id { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
public string Academy { get; set; }
public string Major { get; set; }
public string Class { get; set; }
public string Department { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public DateTime CreationTime { get; set; }
public List<RoleDto>? Roles { get; set; }
}
public class RoleDto
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreationTime { get; set; }
}
5. Register JWT Authentication Service
Install the JWT authentication package:
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Configure JWT settings in appsettings.json:
{
"Authentication": {
"JwtBearer": {
"Issuer": "https://yourdomain.com",
"Audience": "https://api.yourdomain.com",
"SecurityKey": "aVeryLongSecretKeyUsedForSigningTokensAndMustBeAtLeast128Bits",
"ExpirationInDays": 7
}
}
}
Register services in Program.cs:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var jwtConfig = builder.Configuration.GetSection("Authentication:JwtBearer");
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtConfig["Issuer"],
ValidAudience = jwtConfig["Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig["SecurityKey"]))
};
});
builder.Services.AddAuthorization();
6. Implement Login Endpoint
Create a controller to handle authentication requests:
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly SignInManager<User> _signInManager;
private readonly IConfiguration _config;
public AuthController(SignInManager<User> signInManager, IConfiguration config)
{
_signInManager = signInManager;
_config = config;
}
[HttpPost("login")]
[AllowAnonymous]
public async Task<IActionResult> Authenticate([FromBody] LoginRequest request)
{
var result = await _signInManager.PasswordSignInAsync(
request.Username,
request.Password,
isPersistent: false,
lockoutOnFailure: false
);
if (result.Succeeded)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, request.Username),
new Claim(ClaimTypes.Role, "Admin")
};
var securityKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_config["Authentication:JwtBearer:SecurityKey"])
);
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _config["Authentication:JwtBearer:Issuer"],
audience: _config["Authentication:JwtBearer:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(7),
signingCredentials: credentials
);
var tokenHandler = new JwtSecurityTokenHandler();
var jwt = tokenHandler.WriteToken(token);
return Ok(new { Token = jwt });
}
return BadRequest(new { Message = "Invalid credentials." });
}
}
7. Test the Endpoint
Start the application and send a POST request to /api/auth/login with the following body:
{
"Username": "admin",
"Password": "securePassword123"
}
Upon successful validation, the system returns a signed JWT token.