ASP.NET Core Identity

authentication libraryASP.NET Core.NETC#authenticationauthorizationEntity FrameworkIdentity FrameworkMicrosoft

Authentication Library

ASP.NET Core Identity

Overview

ASP.NET Core Identity is Microsoft's official identity management framework for ASP.NET Core, providing comprehensive authentication and authorization capabilities. As of 2025, it supports the latest .NET 9.0 and includes advanced features like PAR (Pushed Authorization Request), OAuth integration, and enhanced Windows authentication. With built-in UI scaffolding, support for external authentication providers (Facebook, Google, Microsoft Account, Twitter), Entity Framework Core integration, JWT and API authentication support, and enterprise-ready security features including 2FA via SMS and authenticator apps, ASP.NET Core Identity offers a complete solution for modern web application security. The framework seamlessly integrates with web applications, APIs, and Single Page Applications (SPAs), providing Role-Based Access Control (RBAC) and comprehensive permission management.

Details

ASP.NET Core Identity 9.0 series leverages the latest .NET 9 capabilities to provide enterprise-grade authentication solutions. The architecture consists of Identity API, Entity Framework Core integration, customizable UI scaffolding, and extensive configuration options. Two-factor authentication (2FA) supports various methods including SMS, email, and authenticator app verification, while the security framework includes account lockout protection, password complexity policies, and comprehensive audit logging. The system is designed for scalability with support for multi-tenant architectures, API-first design for SPA integration, and Role-Based Access Control (RBAC) with fine-grained permission management.

Key Features

  • .NET 9 Support: PAR, OpenID Connect enhancements, and Windows authentication improvements
  • Comprehensive Security: Account lockout, password policies, audit logging, and session management
  • External Provider Integration: Facebook, Google, Microsoft, Twitter OAuth integration
  • Two-Factor Authentication: SMS, email, and authenticator app 2FA support
  • Entity Framework Integration: Seamless database integration with migration support
  • API Authentication: JWT, bearer tokens, and API key authentication

Advantages and Disadvantages

Advantages

  • Microsoft official framework ensuring enterprise-grade reliability and long-term support
  • Deep integration with .NET ecosystem providing excellent development experience
  • Entity Framework Core integration offers seamless database operations
  • Rich UI scaffolding enables rapid authentication implementation
  • Comprehensive security features with built-in protection against common attacks
  • Extensive external provider support with simple configuration

Disadvantages

  • Limited to .NET ecosystem (no support for other platforms)
  • Complex initial setup may require significant development effort
  • Entity Framework dependency may not suit all database approaches
  • Heavily coupled to ASP.NET Core framework limiting architectural flexibility
  • Steep learning curve for developers new to .NET ecosystem
  • UI scaffolding customization can be challenging for complex requirements

Reference Pages

Usage Examples

Project Setup and Identity Configuration

# Create ASP.NET Core Web Application with Individual Authentication
dotnet new webapp -n MyWebApp -au Individual

# Navigate to project directory
cd MyWebApp

# Add required NuGet packages
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

# Update database with Identity schema
dotnet ef database update

Program.cs Configuration (.NET 9 Style)

// Program.cs - ASP.NET Core Identity Configuration
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using MyWebApp.Data;

var builder = WebApplication.CreateBuilder(args);

// Database connection configuration
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") 
    ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

// Identity service configuration
builder.Services.AddDefaultIdentity<IdentityUser>(options => {
    // Password requirements
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = 8;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequireLowercase = true;
    
    // User requirements
    options.User.RequireUniqueEmail = true;
    options.User.AllowedUserNameCharacters = 
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    
    // Lockout configuration
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;
    
    // Email confirmation requirements
    options.SignIn.RequireConfirmedEmail = true;
    
    // Two-factor authentication
    options.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

// Configure application cookie
builder.Services.ConfigureApplicationCookie(options => {
    options.LoginPath = "/Identity/Account/Login";
    options.LogoutPath = "/Identity/Account/Logout";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.ExpireTimeSpan = TimeSpan.FromDays(7);
    options.SlidingExpiration = true;
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
});

// Add MVC services
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure middleware pipeline
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

// Authentication and authorization middleware
app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();

Custom User Model and DbContext

// Models/ApplicationUser.cs - Custom User Model
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;

public class ApplicationUser : IdentityUser
{
    [Required]
    [MaxLength(50)]
    public string FirstName { get; set; } = string.Empty;
    
    [Required]
    [MaxLength(50)]
    public string LastName { get; set; } = string.Empty;
    
    [MaxLength(200)]
    public string? ProfilePicture { get; set; }
    
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    
    public DateTime? LastLoginAt { get; set; }
    
    [MaxLength(500)]
    public string? Bio { get; set; }
    
    // Navigation properties
    public virtual ICollection<UserRole> UserRoles { get; set; } = new List<UserRole>();
}

// Models/UserRole.cs - Custom Role Model
public class UserRole : IdentityRole
{
    [MaxLength(500)]
    public string? Description { get; set; }
    
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    
    public bool IsActive { get; set; } = true;
}

// Data/ApplicationDbContext.cs - Custom DbContext
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, UserRole, string>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
    
    // Additional DbSets for custom entities
    public DbSet<UserProfile> UserProfiles { get; set; }
    public DbSet<AuditLog> AuditLogs { get; set; }
    
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        
        // Custom configurations
        builder.Entity<ApplicationUser>(entity =>
        {
            entity.Property(e => e.FirstName).IsRequired().HasMaxLength(50);
            entity.Property(e => e.LastName).IsRequired().HasMaxLength(50);
            entity.HasIndex(e => e.Email).IsUnique();
        });
        
        builder.Entity<UserRole>(entity =>
        {
            entity.Property(e => e.Description).HasMaxLength(500);
        });
        
        // Seed default roles
        builder.Entity<UserRole>().HasData(
            new UserRole { Id = "1", Name = "Administrator", NormalizedName = "ADMINISTRATOR", Description = "Full system access" },
            new UserRole { Id = "2", Name = "User", NormalizedName = "USER", Description = "Standard user access" },
            new UserRole { Id = "3", Name = "Manager", NormalizedName = "MANAGER", Description = "Management level access" }
        );
    }
}

Authentication Controller and Actions

// Controllers/AuthController.cs - Custom Authentication Controller
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly ILogger<AuthController> _logger;
    
    public AuthController(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager,
        ILogger<AuthController> logger)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
    }
    
    [HttpPost("register")]
    public async Task<IActionResult> Register([FromBody] RegisterModel model)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
        
        var user = new ApplicationUser
        {
            UserName = model.Email,
            Email = model.Email,
            FirstName = model.FirstName,
            LastName = model.LastName,
            CreatedAt = DateTime.UtcNow
        };
        
        var result = await _userManager.CreateAsync(user, model.Password);
        
        if (result.Succeeded)
        {
            // Add user to default role
            await _userManager.AddToRoleAsync(user, "User");
            
            // Generate email confirmation token
            var emailToken = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            
            // Log successful registration
            _logger.LogInformation("User {Email} registered successfully", model.Email);
            
            return Ok(new { 
                Message = "Registration successful", 
                UserId = user.Id,
                EmailConfirmationRequired = true 
            });
        }
        
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
        
        return BadRequest(ModelState);
    }
    
    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginModel model)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
        
        var user = await _userManager.FindByEmailAsync(model.Email);
        if (user == null)
        {
            return Unauthorized(new { Message = "Invalid credentials" });
        }
        
        var result = await _signInManager.PasswordSignInAsync(
            user, model.Password, model.RememberMe, lockoutOnFailure: true);
        
        if (result.Succeeded)
        {
            // Update last login time
            user.LastLoginAt = DateTime.UtcNow;
            await _userManager.UpdateAsync(user);
            
            _logger.LogInformation("User {Email} logged in successfully", model.Email);
            
            return Ok(new { 
                Message = "Login successful",
                UserId = user.Id,
                RequiresTwoFactor = false
            });
        }
        
        if (result.RequiresTwoFactor)
        {
            return Ok(new { 
                Message = "Two-factor authentication required",
                RequiresTwoFactor = true 
            });
        }
        
        if (result.IsLockedOut)
        {
            return BadRequest(new { Message = "Account locked due to multiple failed attempts" });
        }
        
        return Unauthorized(new { Message = "Invalid credentials" });
    }
    
    [HttpPost("logout")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout()
    {
        await _signInManager.SignOutAsync();
        _logger.LogInformation("User logged out");
        return Ok(new { Message = "Logout successful" });
    }
    
    [HttpPost("forgot-password")]
    public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordModel model)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
        
        var user = await _userManager.FindByEmailAsync(model.Email);
        if (user == null)
        {
            // Don't reveal that the user does not exist
            return Ok(new { Message = "If the email exists, a reset link has been sent" });
        }
        
        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
        
        // Here you would send the email with the reset link
        // await _emailSender.SendPasswordResetEmailAsync(user.Email, token);
        
        _logger.LogInformation("Password reset requested for {Email}", model.Email);
        
        return Ok(new { Message = "If the email exists, a reset link has been sent" });
    }
}

// Models for API requests
public class RegisterModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;
    
    [Required]
    [StringLength(100, MinimumLength = 8)]
    public string Password { get; set; } = string.Empty;
    
    [Required]
    [Compare("Password")]
    public string ConfirmPassword { get; set; } = string.Empty;
    
    [Required]
    [MaxLength(50)]
    public string FirstName { get; set; } = string.Empty;
    
    [Required]
    [MaxLength(50)]
    public string LastName { get; set; } = string.Empty;
}

public class LoginModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;
    
    [Required]
    public string Password { get; set; } = string.Empty;
    
    public bool RememberMe { get; set; }
}

public class ForgotPasswordModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;
}

Two-Factor Authentication Implementation

// Controllers/TwoFactorController.cs - 2FA Implementation
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class TwoFactorController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    
    public TwoFactorController(
        UserManager<ApplicationUser> userManager,
        SignInManager<ApplicationUser> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }
    
    [HttpPost("enable")]
    public async Task<IActionResult> EnableTwoFactor()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
            return NotFound();
        
        await _userManager.SetTwoFactorEnabledAsync(user, true);
        
        // Generate QR code for authenticator app setup
        var key = await _userManager.GetAuthenticatorKeyAsync(user);
        if (string.IsNullOrEmpty(key))
        {
            await _userManager.ResetAuthenticatorKeyAsync(user);
            key = await _userManager.GetAuthenticatorKeyAsync(user);
        }
        
        var qrCodeUrl = GenerateQrCodeUri(user.Email!, key!);
        
        return Ok(new { 
            AuthenticatorKey = key,
            QrCodeUrl = qrCodeUrl,
            Message = "Two-factor authentication enabled" 
        });
    }
    
    [HttpPost("verify")]
    public async Task<IActionResult> VerifyTwoFactor([FromBody] VerifyTwoFactorModel model)
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
            return NotFound();
        
        var isValid = await _userManager.VerifyTwoFactorTokenAsync(
            user, _userManager.Options.Tokens.AuthenticatorTokenProvider, model.Code);
        
        if (isValid)
        {
            // Generate recovery codes
            var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
            
            return Ok(new { 
                Message = "Two-factor authentication verified",
                RecoveryCodes = recoveryCodes 
            });
        }
        
        return BadRequest(new { Message = "Invalid verification code" });
    }
    
    [HttpPost("disable")]
    public async Task<IActionResult> DisableTwoFactor()
    {
        var user = await _userManager.GetUserAsync(User);
        if (user == null)
            return NotFound();
        
        await _userManager.SetTwoFactorEnabledAsync(user, false);
        await _userManager.ResetAuthenticatorKeyAsync(user);
        
        return Ok(new { Message = "Two-factor authentication disabled" });
    }
    
    private string GenerateQrCodeUri(string email, string key)
    {
        return string.Format(
            "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6",
            "MyWebApp",
            email,
            key);
    }
}

public class VerifyTwoFactorModel
{
    [Required]
    [StringLength(7, MinimumLength = 6)]
    public string Code { get; set; } = string.Empty;
}

Role-Based Authorization and Custom Claims

// Services/UserRoleService.cs - Role Management Service
public interface IUserRoleService
{
    Task<bool> AssignRoleToUserAsync(string userId, string roleName);
    Task<bool> RemoveRoleFromUserAsync(string userId, string roleName);
    Task<IList<string>> GetUserRolesAsync(string userId);
    Task<bool> CreateRoleAsync(string roleName, string description);
}

public class UserRoleService : IUserRoleService
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<UserRole> _roleManager;
    
    public UserRoleService(
        UserManager<ApplicationUser> userManager,
        RoleManager<UserRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }
    
    public async Task<bool> AssignRoleToUserAsync(string userId, string roleName)
    {
        var user = await _userManager.FindByIdAsync(userId);
        if (user == null) return false;
        
        var roleExists = await _roleManager.RoleExistsAsync(roleName);
        if (!roleExists) return false;
        
        var result = await _userManager.AddToRoleAsync(user, roleName);
        return result.Succeeded;
    }
    
    public async Task<bool> RemoveRoleFromUserAsync(string userId, string roleName)
    {
        var user = await _userManager.FindByIdAsync(userId);
        if (user == null) return false;
        
        var result = await _userManager.RemoveFromRoleAsync(user, roleName);
        return result.Succeeded;
    }
    
    public async Task<IList<string>> GetUserRolesAsync(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
        if (user == null) return new List<string>();
        
        return await _userManager.GetRolesAsync(user);
    }
    
    public async Task<bool> CreateRoleAsync(string roleName, string description)
    {
        var roleExists = await _roleManager.RoleExistsAsync(roleName);
        if (roleExists) return false;
        
        var role = new UserRole
        {
            Name = roleName,
            Description = description,
            CreatedAt = DateTime.UtcNow
        };
        
        var result = await _roleManager.CreateAsync(role);
        return result.Succeeded;
    }
}

// Authorization/CustomAuthorizationHandler.cs - Custom Permission Authorization
public class PermissionRequirement : IAuthorizationRequirement
{
    public string Permission { get; }
    
    public PermissionRequirement(string permission)
    {
        Permission = permission;
    }
}

public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
    private readonly UserManager<ApplicationUser> _userManager;
    
    public PermissionAuthorizationHandler(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }
    
    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        PermissionRequirement requirement)
    {
        var user = await _userManager.GetUserAsync(context.User);
        if (user == null)
        {
            context.Fail();
            return;
        }
        
        var userRoles = await _userManager.GetRolesAsync(user);
        var userClaims = await _userManager.GetClaimsAsync(user);
        
        // Check if user has required permission through claims or roles
        if (userClaims.Any(c => c.Type == "permission" && c.Value == requirement.Permission) ||
            userRoles.Contains("Administrator"))
        {
            context.Succeed(requirement);
        }
    }
}

// Usage in controller
[Authorize(Policy = "RequireUserManagement")]
[HttpGet("users")]
public async Task<IActionResult> GetUsers()
{
    // Implementation
    return Ok();
}

External Authentication Provider Configuration

// Program.cs - External Authentication Configuration
// Google OAuth configuration
builder.Services.AddAuthentication()
    .AddGoogle(googleOptions =>
    {
        googleOptions.ClientId = builder.Configuration["Authentication:Google:ClientId"]!;
        googleOptions.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"]!;
        googleOptions.Scope.Add("profile");
        googleOptions.Scope.Add("email");
        
        googleOptions.Events.OnCreatingTicket = async context =>
        {
            // Custom logic for handling Google authentication
            var user = context.Principal;
            // Store additional user information
        };
    })
    .AddFacebook(facebookOptions =>
    {
        facebookOptions.AppId = builder.Configuration["Authentication:Facebook:AppId"]!;
        facebookOptions.AppSecret = builder.Configuration["Authentication:Facebook:AppSecret"]!;
        facebookOptions.Scope.Add("email");
    })
    .AddMicrosoftAccount(microsoftOptions =>
    {
        microsoftOptions.ClientId = builder.Configuration["Authentication:Microsoft:ClientId"]!;
        microsoftOptions.ClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"]!;
    });

// Authorization policies
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdministrator", policy =>
        policy.RequireRole("Administrator"));
    
    options.AddPolicy("RequireUserManagement", policy =>
        policy.Requirements.Add(new PermissionRequirement("user_management")));
    
    options.AddPolicy("RequireEmailConfirmed", policy =>
        policy.RequireClaim("email_verified", "true"));
});

// Register custom services
builder.Services.AddScoped<IUserRoleService, UserRoleService>();
builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();