Real-world applications frequently need more precise control, even while Role-Based Authorization manages broad access levels like Admin or User. What happens if access is contingent on a number of factors, such as the user's age, location, or ownership of the particular resource they are attempting to modify? ASP.NET Core offers Policy-Based Authorization for these complex situations. A policy, statements, assertions, assertions, constructed.

This is a building that covers everything from scratch.

Step 1: Enabling Role Management in Program.cs
By default, ASP.NET Core Identity configures user authentication stores. To support roles, you must chain .AddRoles<IdentityRole>() into your service registration so that Entity Framework Core can scaffold and manage the underlying role tables (AspNetRoles, AspNetUserRoles).

Update your Program.cs configuration:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using MvcAuthenticationDemo.Data;


var builder = WebApplication.CreateBuilder(args);

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

// Add Identity with Role Support
builder.Services.AddDefaultIdentity<IdentityUser>(options => 
{
    options.SignIn.RequireConfirmedAccount = false;
    options.Password.RequireDigit = true;
    options.Password.RequiredLength = 6;
})
.AddRoles<IdentityRole>() // <-- Enables Role Manager services
.AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddControllersWithViews();

var app = builder.Build();

// Pipeline middleware configuration...
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

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


Step 2: Bootstrapping Roles and an Admin User via Seeding
To prevent your application from launching with zero roles or administrative accounts, you can seed default roles (Admin, User) and create a default super-admin user directly when the application starts up.

Add this database seeding block right before app.Run(); in Program.cs:
// --- Role and Admin Seeding Block ---
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
        var userManager = services.GetRequiredService<UserManager<IdentityUser>>();

        // 1. Create Default Roles
        string[] roleNames = { "Admin", "Manager", "User" };
        foreach (var roleName in roleNames)
        {
            if (!await roleManager.RoleExistsAsync(roleName))
            {
                await roleManager.CreateAsync(new IdentityRole(roleName));
            }
        }

        // 2. Create Default Admin User
        string adminEmail = "[email protected]";
        var adminUser = await userManager.FindByEmailAsync(adminEmail);
        if (adminUser == null)
        {
            adminUser = new IdentityUser 
            { 
                UserName = adminEmail, 
                Email = adminEmail, 
                EmailConfirmed = true 
            };
            
            // Create user with a secure temporary password
            var createAdmin = await userManager.CreateAsync(adminUser, "Admin@12345");
            if (createAdmin.Succeeded)
            {
                await userManager.AddToRoleAsync(adminUser, "Admin");
            }
        }
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred while seeding the database with roles.");
    }
}
// ------------------------------------

app.Run();


Step 3: Protecting Controllers and Action Methods
Once roles are established, securing your endpoints is straightforward using the built-in [Authorize] attribute paired with the Roles parameter. ASP.NET Core will automatically evaluate the authenticated user's claims and reject unauthorized requests with a 403 Forbidden response.

Securing an Entire Controller

If an entire dashboard or management suite should only be accessible by administrators, apply the attribute at the class level:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
    public IActionResult Index()
    {
        return View(); // Only accessible by users in the 'Admin' role
    }
}


Securing Specific Action Methods (Multiple Roles)
You can target individual actions and allow multiple comma-separated roles access to specific methods:
[Authorize(Roles = "Admin, Manager")]
[HttpPost]
public IActionResult DeleteRecord(int id)
{
    // Accessible by users in either 'Admin' or 'Manager' roles
    return RedirectToAction("Index");
}

Step 4: Conditionally Rendering UI Elements in Razor Views
Authorization isn't just about blocking backend controller routes; it's also about keeping unauthorized options hidden from the user interface to improve user experience.
You can check whether a user belongs to a role directly inside your Razor layouts or views using User.IsInRole():

HTML
<ul class="navbar-nav ms-auto">
    <li class="nav-item">
        <a class="nav-link text-dark" asp-controller="Home" asp-action="Index">Home</a>
    </li>

    @if (User.Identity != null && User.Identity.IsAuthenticated)
    {
        <!-- Show for any logged-in user -->
        <li class="nav-item">
            <span class="nav-link text-muted">Hello, @User.Identity.Name</span>
        </li>

        @if (User.IsInRole("Admin"))
        {
            <!-- Show exclusively for Administrators -->
            <li class="nav-item">
                <a class="nav-link text-danger fw-bold" asp-controller="Admin" asp-action="Index">Admin Panel</a>
            </li>
        }

        <li class="nav-item">
            <form asp-controller="Account" asp-action="Logout" method="post">
                <button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
            </form>
        </li>
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Login</a>
        </li>
    }
</ul>


Conclusion
By integrating ASP.NET Core Identity roles with Program.cs configurations, [Authorize(Roles = "...")] attributes, and conditional Razor views, you establish a resilient, secure foundation for multi-tiered application architecture.