Introduction

Securing your web applications is paramount. ASP.NET Core provides a robust and flexible framework for implementing authentication and authorization, ensuring that only legitimate users can access specific resources.

Authentication is the process of verifying who a user is. Authorization is the process of determining what an authenticated user is allowed to do.

Authentication

ASP.NET Core uses a middleware pipeline to handle authentication. The core concepts involve:

  • Authentication Schemes: Define how authentication is performed (e.g., cookies, JWT, external providers).
  • Authentication Handler: The specific logic for a given scheme.
  • IAuthenticationService: The service that orchestrates authentication.

In your Program.cs (or Startup.cs in older versions), you configure authentication services:

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Login";
        options.LogoutPath = "/Account/Logout";
    });

builder.Services.AddControllersWithViews();

var app = builder.Build();

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

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

app.UseRouting();

app.UseAuthentication(); // Must come before UseAuthorization
app.UseAuthorization();

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

app.Run();

Authorization

Authorization determines if an authenticated user has permission to perform an action or access a resource. ASP.NET Core supports declarative and imperative authorization.

Declarative Authorization: Applied using attributes on controllers or actions.

using Microsoft.AspNetCore.Mvc;

[Authorize] // Requires authentication
public class DashboardController : Controller
{
    [Authorize("AdminOnly")] // Requires a specific policy
    public IActionResult AdminPanel()
    {
        return View();
    }

    public IActionResult UserProfile()
    {
        return View();
    }
}

Ensure app.UseAuthorization(); is called in your pipeline.

ASP.NET Core Identity

ASP.NET Core Identity is a membership system that handles user accounts, passwords, roles, and claims. It's a powerful framework built on top of ASP.NET Core's authentication and authorization features.

Key Components:

  • UserManager<TUser>: Manages users.
  • SignInManager<TUser>: Handles user sign-in/sign-out.
  • RoleManager<TRole>: Manages roles.
  • DbContext (e.g., IdentityDbContext): For storing user data in a database.

Setup:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

// Configure cookie authentication and identity options as shown previously
builder.Services.AddAuthentication(options => { /* ... */ })
    .AddCookie(options => { /* ... */ });
builder.Services.AddAuthorization(); // Required for authorization

You'll need to create a DbContext class inheriting from IdentityDbContext and run migrations to set up the database schema.

Role-Based Authorization

Assign users to roles (e.g., "Admin", "User", "Editor") and grant access based on these roles.

Applying Role Authorization:

[Authorize(Roles = "Admin,Editor")]
public class AdminController : Controller
{
    // ...
}

You can also check roles in code:

if (User.IsInRole("Admin"))
{
    // User is an admin
}

Policy-Based Authorization

Policies offer a more granular and flexible way to define authorization requirements beyond simple roles. A policy can combine roles, claims, or custom logic.

Defining a Policy:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole", policy =>
        policy.RequireRole("Admin"));

    options.AddPolicy("MinimumAge", policy =>
        policy.RequireAssertion(context =>
        {
            var ageClaim = context.User.FindFirst("age");
            if (ageClaim == null) return false;
            return int.Parse(ageClaim.Value) >= 18;
        }));
});

Applying a Policy:

[Authorize(Policy = "RequireAdminRole")]
public class SuperAdminController : Controller
{
    // ...
}

Claims-Based Authorization

Claims are name-value pairs that represent an assertion about a subject (user). Examples include "name", "email", "age", or custom entitlements.

Adding Claims: Claims are typically added during the sign-in process or can be managed via UserManager.

Authorizing based on Claims:

[Authorize(Policy = "MinimumAge")] // Using the policy defined above
public class AdultContentController : Controller
{
    // ...
}

You can also use RequireClaim directly in policies or check claims in code:

[Authorize(Policy = "MustHaveFirstName")]
public class ProfileController : Controller
{
    // ...
}
// Policy definition in Program.cs:
// options.AddPolicy("MustHaveFirstName", policy => policy.RequireClaim("given_name"));

OAuth and OpenID Connect

ASP.NET Core provides excellent support for integrating with OAuth 2.0 and OpenID Connect (OIDC) providers like Google, Facebook, Microsoft Identity Platform, etc. This allows users to sign in using their existing accounts.

Configuration Example (Google):

builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    })
    .AddCookie(); // Ensure cookie auth is also configured

You'll need to create an `AccountController` with actions like `Login`, `Logout`, and handle the callback from the external provider.

Best Practices

  • HTTPS Everywhere: Always use HTTPS to protect credentials and sensitive data.
  • Secure Password Storage: ASP.NET Core Identity handles password hashing securely.
  • Least Privilege: Grant only the necessary permissions to users.
  • Centralize Logic: Define policies and authorization requirements in a consistent place (e.g., Program.cs or a dedicated authorization service).
  • Input Validation: Always validate user input, especially when it relates to security.
  • Error Handling: Implement graceful error handling for unauthorized access attempts.
  • Regular Updates: Keep your ASP.NET Core and dependency packages updated to benefit from security patches.