EthicsComputerCourses
Join training +91 90532 10052

Authentication with ASP.NET Core Identity

ASP.NET Core · Lesson 21 of 29 Advanced 8 min read Updated

A clinic's reception staff must log in before they can see the day's patient list, and a patient may want an account to view past visits. Writing login code yourself is risky: passwords must be hashed correctly, failed attempts limited, sessions protected and reset links secured. ASP.NET Core Identity is a ready-made membership system that does all of this.

In this lesson you will learn the difference between authentication and authorization, create an app with Identity from a template, understand what it adds, tune password and lockout rules, protect pages, use roles, and see the options for single-page and mobile apps.

Authentication and authorization

Two words that sound alike but mean different things:

  • Authentication answers "who are you?" The user proves their identity, for example with an email and password, and the app remembers them, usually with an encrypted cookie.
  • Authorization answers "what may you do?" Once the app knows the user, it decides whether they can open the staff dashboard or cancel an appointment.

In ASP.NET Core the signed-in user is a ClaimsPrincipal, available as User in pages and controllers and as a ClaimsPrincipal parameter in minimal APIs. A claim is one fact about the user: their ID, email, name or a role such as Receptionist.

Creating an app with Identity

The quickest start is a template with individual accounts:

Terminal
dotnet new webapp --auth Individual -o ClinicStaff
cd ClinicStaff
dotnet run

The template stores users in a SQLite database by default (add --use-local-db for SQL Server LocalDB on Windows). It gives you, ready to use:

  • An ApplicationDbContext that inherits from IdentityDbContext, with tables such as AspNetUsers, AspNetRoles and AspNetUserRoles, plus a migration that creates them.
  • Register, login, logout, forgot-password, email confirmation, two-factor and account management pages at URLs such as /Identity/Account/Login. They come from a Razor Class Library, so the files are not in your project until you scaffold the ones you want to customise.
  • A _LoginPartial.cshtml in the layout that shows "Register" and "Login", or "Hello, name" and "Logout".

The registration in Program.cs looks like this:

C#
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

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

builder.Services.AddDefaultIdentity<IdentityUser>(options =>
        options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddRazorPages();

AddDefaultIdentity registers the user manager, sign-in manager, password hasher, cookie authentication and the default UI. AddEntityFrameworkStores tells Identity to keep users in your EF Core context. With RequireConfirmedAccount = true, new users must confirm their email before logging in; until you configure a real email sender, the template shows a confirmation link on screen for development.

What Identity does for you

  • Password hashing: passwords are never stored. Identity stores a salted, deliberately slow hash (PBKDF2), so a stolen database does not reveal passwords.
  • Password rules: by default at least 6 characters with an uppercase letter, a lowercase letter, a digit and a non-alphanumeric character.
  • Lockout: after 5 failed attempts an account can be locked for 5 minutes, slowing password guessing.
  • Security stamps: changing a password invalidates other sessions.
  • Two-factor authentication with authenticator apps, and recovery codes.
  • Passkeys (new in .NET 10): passwordless sign-in with a fingerprint, face unlock or a security key, based on the WebAuthn standard. The Blazor Web App template includes passkey registration and login.

Tune the rules to your own policy:

C#
builder.Services.Configure<IdentityOptions>(options =>
{
    options.Password.RequiredLength = 10;
    options.Password.RequireNonAlphanumeric = false;   // length matters more than symbols
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
    options.User.RequireUniqueEmail = true;
});

Protecting pages

Mark a page model or controller with [Authorize], and anonymous visitors are redirected to the login page, then sent back after logging in:

C#
using Microsoft.AspNetCore.Authorization;

[Authorize]
public class TodayModel(ApplicationDbContext db) : PageModel
{
    public string StaffName { get; private set; } = "";

    public void OnGet() => StaffName = User.Identity?.Name ?? "";
}

For whole areas, use Razor Pages conventions in Program.cs instead of repeating the attribute:

C#
builder.Services.AddRazorPages(options =>
{
    options.Conventions.AuthorizeFolder("/Staff");
    options.Conventions.AllowAnonymousToPage("/Staff/Help");
});

Every page under Pages/Staff now needs a signed-in user, except the help page. [AllowAnonymous] does the same for a single page or action.

Roles

A role groups permissions: Receptionist, Doctor, Admin. Enable roles by adding .AddRoles<IdentityRole>() before AddEntityFrameworkStores, then create roles and assign users with RoleManager and UserManager, for example once at start-up:

C#
builder.Services.AddDefaultIdentity<IdentityUser>(o => o.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var roles = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    foreach (var role in new[] { "Receptionist", "Doctor", "Admin" })
    {
        if (!await roles.RoleExistsAsync(role))
        {
            await roles.CreateAsync(new IdentityRole(role));
        }
    }
}

Assign a role with await userManager.AddToRoleAsync(user, "Receptionist"), and restrict a page with [Authorize(Roles = "Receptionist,Admin")] (either role is enough). Role checks in Razor views use User.IsInRole("Doctor"). A user's roles are copied into their cookie at login, so a role change takes effect after they log in again.

APIs, SPAs and mobile apps

Cookies suit server-rendered sites on one domain. For a React front end or an Android app, .NET 8 added Identity API endpoints:

C#
builder.Services.AddIdentityApiEndpoints<IdentityUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthorization();

var app = builder.Build();
app.MapGroup("/account").MapIdentityApi<IdentityUser>();

This maps JSON endpoints such as POST /account/register and POST /account/login. Login returns either a cookie (?useCookies=true) or an access token and refresh token. These tokens are Identity's own format, not JWTs, and are meant for simple first-party apps. For larger systems, several APIs or third-party clients, use a dedicated identity provider that issues standard JWTs, as described in JWT Bearer Tokens & Authorization. Social logins (Google, Microsoft, Facebook) and company single sign-on with OpenID Connect plug into the same authentication system.

Common mistakes

  • Storing passwords yourself. Use Identity or an identity provider; never hash passwords with your own code.
  • Leaving lockout off. Change lockoutOnFailure to true on the login page.
  • Forgetting to send real emails. With RequireConfirmedAccount, users cannot log in until you implement IEmailSender with an email service.
  • Expecting role changes immediately. The cookie keeps old roles until the user signs in again (or you refresh their sign-in).
  • Using Identity API tokens as if they were JWTs. Other services cannot validate them.

Key points

  • Authentication proves who the user is; authorization decides what they may do. Users are ClaimsPrincipal objects with claims.
  • dotnet new webapp --auth Individual adds Identity with EF Core storage, UI pages and cookie login.
  • Identity hashes passwords, enforces password and lockout rules, and supports 2FA and, from .NET 10, passkeys.
  • Protect pages with [Authorize] or folder conventions, and group permissions with roles and [Authorize(Roles = ...)].
  • MapIdentityApi serves JSON login for SPAs and mobile apps; its tokens are not JWTs.

Exercise

Configure Identity for a coaching institute in Lucknow: passwords of at least 12 characters without a forced symbol, unique emails, lockout for 10 minutes after 3 failures, and roles Teacher and Student. Protect every page under Pages/Teachers for the Teacher role, but keep Pages/Teachers/Timetable open to everyone.

Show solution
C#
builder.Services.AddDefaultIdentity<IdentityUser>(o => o.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.Configure<IdentityOptions>(o =>
{
    o.Password.RequiredLength = 12;
    o.Password.RequireNonAlphanumeric = false;
    o.User.RequireUniqueEmail = true;
    o.Lockout.MaxFailedAccessAttempts = 3;
    o.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
});

builder.Services.AddRazorPages(o =>
{
    o.Conventions.AuthorizeFolder("/Teachers", "TeachersOnly");
    o.Conventions.AllowAnonymousToPage("/Teachers/Timetable");
});

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("TeachersOnly", p => p.RequireRole("Teacher"));

AuthorizeFolder accepts a policy name, so the folder requires the Teacher role through the TeachersOnly policy. Create the two roles at start-up with RoleManager, as shown in the lesson, and remember to scaffold the Login page and set lockoutOnFailure: true so the lockout settings take effect.

Quick Quiz

1. What is the difference between authentication and authorization?

2. How does Identity store passwords?

3. What does options.Conventions.AuthorizeFolder("/Staff") do?

4. Are the tokens returned by MapIdentityApi login endpoints JWTs?

Learn ASP.NET Core with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call