ASP.NET Core  

Mastering Role-Based and Policy-Based Authorization in ASP.NET Core

Introduction

Authorization is a key part of application security that ensures users can only access what they are permitted to. In ASP.NET Core, Microsoft provides a flexible, extensible authorization framework supporting both Role-Based and Policy-Based approaches.

While role-based authorization is straightforward and ideal for simple scenarios, policy-based authorization provides more granular, contextual, and scalable control — especially useful in enterprise-grade applications.

In this article, we’ll explore both methods in detail, their use cases, and how to implement them effectively.

1. Understanding Authorization in ASP.NET Core

ASP.NET Core divides security into two major concepts:

  • Authentication: Verifies who the user is.

  • Authorization: Determines what the authenticated user is allowed to do.

Once a user is authenticated (using JWT, Cookies, OAuth, etc.), the authorization system checks whether they have permission to access specific resources or perform certain actions.

2. Role-Based Authorization

a. When to Use

Role-based authorization works best when your permissions are simple and static — for example:

  • Admins can manage users.

  • Managers can approve requests.

  • Employees can view data.

b. Setting Up Roles

Add claims with roles during authentication (e.g., in JWT or Identity setup):

var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, "rajesh"),
    new Claim(ClaimTypes.Role, "Admin")
};

var identity = new ClaimsIdentity(claims, "Auth");
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(principal);

c. Applying Role-Based Authorization

You can restrict access using the [Authorize] attribute:

[Authorize(Roles = "Admin")]
[ApiController]
[Route("api/[controller]")]
public class UserManagementController : ControllerBase
{
    [HttpGet("users")]
    public IActionResult GetAllUsers()
    {
        return Ok("Access granted to Admin.");
    }
}

You can also allow multiple roles:

[Authorize(Roles = "Admin,Manager")]

3. Policy-Based Authorization

a. When to Use

When your access control needs to depend on business rules or dynamic conditions, policy-based authorization is ideal.
Example scenarios:

  • A manager can approve requests only for their own department.

  • A user can edit a document only if they created it.

b. Defining a Policy

Register a policy in the Program.cs or Startup.cs file:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireHRDepartment", policy =>
        policy.RequireClaim("Department", "HR"));
});

This policy ensures only users with the claim "Department" = "HR" can access certain endpoints.

c. Applying the Policy

Use the [Authorize(Policy = "PolicyName")] attribute:

[Authorize(Policy = "RequireHRDepartment")]
[HttpGet("hr-data")]
public IActionResult GetHRData()
{
    return Ok("Access granted to HR department users.");
}

4. Creating Custom Authorization Handlers

For more complex conditions, use Authorization Handlers.

a. Define a Requirement

public class MinimumExperienceRequirement : IAuthorizationRequirement
{
    public int Years { get; }
    public MinimumExperienceRequirement(int years)
    {
        Years = years;
    }
}

b. Create a Handler

public class MinimumExperienceHandler : AuthorizationHandler<MinimumExperienceRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumExperienceRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == "YearsOfExperience"))
            return Task.CompletedTask;

        var experience = int.Parse(context.User.FindFirst("YearsOfExperience")!.Value);
        if (experience >= requirement.Years)
            context.Succeed(requirement);

        return Task.CompletedTask;
    }
}

c. Register the Policy

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ExperiencedEmployee", policy =>
        policy.Requirements.Add(new MinimumExperienceRequirement(5)));
});

builder.Services.AddSingleton<IAuthorizationHandler, MinimumExperienceHandler>();

d. Apply the Policy

[Authorize(Policy = "ExperiencedEmployee")]
[HttpGet("advanced-reports")]
public IActionResult GetAdvancedReports()
{
    return Ok("Access granted for users with 5+ years of experience.");
}

5. Combining Role-Based and Policy-Based Authorization

You can also combine both mechanisms:

options.AddPolicy("AdminWithExperience", policy =>
{
    policy.RequireRole("Admin");
    policy.Requirements.Add(new MinimumExperienceRequirement(3));
});

This ensures that only users with the Admin role and at least 3 years of experience can access certain APIs.

6. Common Best Practices

  • Use Policies for Scalability: Prefer policy-based authorization when the logic grows complex.

  • Centralize Rules: Keep policies and handlers in a dedicated folder (e.g., Authorization/).

  • Avoid Hardcoding Roles: Store them in configuration or a database when possible.

  • Leverage Claims: Claims provide flexibility to represent attributes like department, clearance level, or location.

  • Audit Access: Log authorization failures for monitoring and security reviews.

7. Example Real-World Use Case

Let’s say your system has three user types — Admin, Manager, and Employee.
You want to:

  • Allow Admins full access.

  • Allow Managers to approve requests for their team only.

  • Allow Employees to view only their own data.

This can be implemented by combining Role-Based (for Admin) and Policy-Based (for Manager/Employee with contextual logic) authorization together, ensuring clarity and maintainability.

Conclusion

Role-based and policy-based authorization are not competing concepts — they’re complementary tools that together provide flexibility, security, and maintainability in modern ASP.NET Core applications.

  • Use roles when access rules are simple and predefined.

  • Use policies when conditions depend on data, business logic, or dynamic attributes.

By combining both approaches and using custom authorization handlers, you can create a fine-grained access control system that scales with your application’s growth and complexity.