Implementing Authentication in .NET Core Using Identity and JWT

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.

Tags: dotnet-core identity-framework jwt-authentication ef-core Authentication

Posted on Sat, 13 Jun 2026 18:11:55 +0000 by webproclaim