IdentityMVCAugust 2026 · 11 min read

ASP.NET Core Identity Complete Guide: Registration, Login, Roles, and Claims

TL;DR

This is the complete ASP.NET Core Identity guide for .NET 10 MVC: register Identity with EF Core stores, configure password and lockout policies, implement registration and login with SignInManager and UserManager, then layer on roles, claims, and two-factor authentication.

ASP.NET Core Identity is the built-in authentication system for ASP.NET Core. It stores users, passwords, roles, and claims in your own database, protects passwords with modern hashing, and integrates directly with the authorization middleware. This complete guide walks every piece you need for a production MVC application on .NET 10 — setup, registration, login, roles, claims, two-factor authentication, and lockout.

Setting Up ASP.NET Core Identity with EF Core

Install the Microsoft.AspNetCore.Identity.EntityFrameworkCore package and register Identity in Program.cs. The AddIdentity extension wires the user store, sign-in manager, and role manager, while AddEntityFrameworkStores points them at your AppDbContext:

builder.Services.AddDbContext<AppDbContext>(o =>
    o.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

builder.Services
    .AddIdentity<AppUser, IdentityRole>(options =>
    {
        options.Password.RequiredLength = 10;
        options.Password.RequireDigit = true;
        options.Password.RequireUppercase = true;
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
        options.User.RequireUniqueEmail = true;
    })
    .AddEntityFrameworkStores<AppDbContext>()
    .AddDefaultTokenProviders();

The AppUser class extends IdentityUser, which lets you add custom columns such as FullName, AvatarUrl, or a TenantId for multi-tenant apps. Run a migration and the Identity tables — AspNetUsers, AspNetRoles, AspNetUserClaims, and friends — appear in your database.

Registration: Creating Users with UserManager

Registration uses UserManager<AppUser>. After a successful create, you can assign a default role and sign the user in:

public class AccountController : Controller
{
    private readonly UserManager<AppUser> _userManager;
    private readonly SignInManager<AppUser> _signInManager;

    public AccountController(UserManager<AppUser> users, SignInManager<AppUser> signIn)
        => (_userManager, _signInManager) = (users, signIn);

    [HttpPost]
    public async Task<IActionResult> Register(RegisterViewModel model)
    {
        if (!ModelState.IsValid) return View(model);

        var user = new AppUser { UserName = model.Email, Email = model.Email, FullName = model.FullName };
        var result = await _userManager.CreateAsync(user, model.Password);
        if (!result.Succeeded)
        {
            foreach (var error in result.Errors)
                ModelState.AddModelError(string.Empty, error.Description);
            return View(model);
        }

        await _userManager.AddToRoleAsync(user, "Member");
        await _signInManager.SignInAsync(user, isPersistent: true);
        return RedirectToAction(nameof(HomeController.Index), "Home");
    }
}

Note that CreateAsync(user, password) hashes and stores the password for you — you never write a password to the database. The default role assignment keeps new users on the least-privileged path, which is exactly how the MVC EDevKit Basic starter seeds its Guest, Member, and Admin roles.

Login and Logout with SignInManager

Login validates credentials, respects lockout, and sets the authentication cookie. Logout clears it:

[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
    if (!ModelState.IsValid) return View(model);

    var result = await _signInManager.PasswordSignInAsync(
        model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);

    if (result.Succeeded) return RedirectToAction(nameof(HomeController.Index), "Home");
    if (result.IsLockedOut) return View("Lockout");
    if (result.RequiresTwoFactor) return RedirectToAction(nameof(LoginTwoFactor));

    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
    return View(model);
}

[HttpPost]
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    return RedirectToAction(nameof(HomeController.Index), "Home");
}

Notice how the sign-in result tells you exactly what happened — success, lockout, or a required second factor — so the UI can respond correctly instead of showing a generic error.

Roles: Seeding Admin and Enforcing Role-Based Access

Seed roles and an admin user at startup, then protect controllers with [Authorize(Roles = "Admin")]:

public static async Task SeedAsync(IServiceProvider services)
{
    var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
    var userManager = services.GetRequiredService<UserManager<AppUser>>();

    foreach (var role in new[] { "Guest", "Member", "Admin" })
        if (!await roleManager.RoleExistsAsync(role))
            await roleManager.CreateAsync(new IdentityRole(role));

    var admin = await userManager.FindByEmailAsync("admin@root.com");
    if (admin is null)
    {
        admin = new AppUser { UserName = "admin@root.com", Email = "admin@root.com" };
        await userManager.CreateAsync(admin, "Admin@123456");
    }
    if (!await userManager.IsInRoleAsync(admin, "Admin"))
        await userManager.AddToRoleAsync(admin, "Admin");
}

Role attributes give you quick, coarse-grained control: [Authorize] for any signed-in user, [Authorize(Roles = "Admin")] for admins only, and [Authorize(Roles = "Admin,Manager")] for a list of roles. For fine-grained rules, move up to claims and policies.

Claims and Policy-Based Authorization

Claims are facts about a user — such as Department = "Finance" — and policies evaluate claims at runtime:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("FinanceOnly", policy =>
        policy.RequireClaim("Department", "Finance"));

    options.AddPolicy("CanApprove", policy =>
        policy.RequireAssertion(ctx =>
            ctx.User.IsInRole("Admin") ||
            ctx.User.HasClaim(c => c.Type == "CanApprove" && c.Value == "true")));
});

Apply with [Authorize(Policy = "FinanceOnly")] on a controller or action. Claims come from the user profile, from the database, or from an external provider, and you can inject them at login time with UserClaimsPrincipalFactory.

Two-Factor Authentication and Lockout

Identity supports authenticator-app and email-based 2FA out of the box. After login you redirect to a code-verification step:

[HttpPost]
public async Task<IActionResult> LoginTwoFactor(string code)
{
    var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
    if (user is null) return RedirectToAction(nameof(Login));

    var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(
        code, rememberMe: true, rememberClient: false);

    if (result.Succeeded) return RedirectToAction(nameof(HomeController.Index), "Home");
    ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
    return View();
}

Lockout is enforced automatically when lockoutOnFailure: true is used with PasswordSignInAsync. Combined with the lockout options set earlier, five failed attempts blocks the account for fifteen minutes — a simple, effective brute-force defense.

Key Takeaways

  • AddIdentity + AddEntityFrameworkStores gives you users, roles, and claims backed by your own database
  • UserManager creates users and hashes passwords; SignInManager handles login, lockout, and 2FA
  • Seed Guest, Member, and Admin roles and assign new users to the least-privileged role
  • Combine [Authorize(Roles=...)] with claims-based policies for coarse and fine-grained access
  • Password policies, lockout, and two-factor authentication are configuration, not custom code

FAQ

What is the difference between UserManager and SignInManager? UserManager performs user management — create, update, roles, claims, password reset. SignInManager handles the sign-in flow itself — password validation, cookies, lockout, and two-factor verification.

Does ASP.NET Core Identity store passwords in plain text? No. Identity hashes passwords with PBKDF2-style hashing (ASP.NET Core version 2+, ASP.NET Core Identity v3+ uses PBKDF2) and you never store or read the raw password.

Can I use both cookie and JWT authentication with Identity? Yes. Cookie authentication serves the MVC UI while JWT bearer authentication protects API endpoints, sharing the same Identity user store.

How do I add custom fields to the Identity user? Create a class that inherits from IdentityUser, add your properties, register that class in AddIdentity<AppUser, IdentityRole>(), and add a migration.

Study Identity implemented end to end in real MVC source code?

MVC EDevKit Basic ships complete ASP.NET Core Identity: registration, login, roles (Guest/Member/Admin), JWT, Firebase SSO, 2FA, and lockout. $21.

View MVC EDevKit Details