ASP.NET Core  

JWT Session 9: Policy-Based Authorization in ASP.NET Core Web API: Beyond Roles

In the previous article, we implemented Role-Based Authorization using the [Authorize(Roles = "...")] attribute. It worked well for scenarios like allowing only Admins to manage products or Customers to place orders.

However, real-world applications often require more than just roles.

For example:

  • A customer can cancel an order only if it hasn't been shipped.

  • A premium member can access exclusive products.

  • An employee must belong to the Sales department to view sales reports.

  • A user must be at least 18 years old to access certain services.

These rules cannot be expressed using roles alone.

This is where Policy-Based Authorization becomes useful.

In this article, you'll learn what policies are, why they are needed, how to configure them in ASP.NET Core, and how they work internally.

Policy-Based Authorization in ASP.NET Core Web API Beyond Roles

What Is Policy-Based Authorization?

Policy-Based Authorization allows you to define authorization rules based on one or more requirements.

Instead of checking only the user's role, a policy can evaluate:

  • Claims

  • Roles

  • Custom business rules

  • Multiple conditions together

A policy acts as a reusable authorization rule that can be applied to one or many API endpoints.

Why Do We Need Policies?

Let's continue with our SecureShop API.

Suppose the business introduces a Premium Membership.

Only premium members can access exclusive products.

Would creating another role be the right solution?

For example:

  • Customer

  • PremiumCustomer

  • GoldCustomer

  • PlatinumCustomer

As the application grows, managing roles becomes difficult.

Instead, we can keep the user's role as Customer and add another claim:

Membership = Premium

Now the authorization decision becomes:

Is the user a Premium member?

This is a perfect use case for a policy.

Roles vs Policies

Both roles and policies are used for authorization, but they solve different problems.

Role-Based AuthorizationPolicy-Based Authorization
Checks user rolesChecks rules or requirements
Simple to configureMore flexible
Best for broad permissionsBest for business rules
Example: AdminExample: Premium Member

A good practice is:

  • Use roles for user categories.

  • Use policies for business-specific rules.

Creating a Claim for Membership

Suppose the JWT contains the following claim:

new Claim("Membership", "Premium")

Now the authenticated user carries this information inside the JWT.

After authentication, ASP.NET Core makes it available through HttpContext.User.

Step 1: Register a Policy

Open Program.cs.

Add a policy while registering authorization.

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("PremiumOnly", policy =>
    {
        policy.RequireClaim("Membership", "Premium");
    });
});

This creates a policy named PremiumOnly.

The rule is simple:

The user must have a claim named Membership with the value Premium.

Step 2: Apply the Policy

Now protect an endpoint using the policy.

[Authorize(Policy = "PremiumOnly")]
[HttpGet("premium-products")]
public IActionResult GetPremiumProducts()
{
    return Ok();
}

Only users whose JWT contains:

Membership = Premium

can access this endpoint.

How Policy Evaluation Works

Let's understand the internal flow.

Client
   │
   │ JWT
   ▼
Authentication Middleware
   │
   ▼
Validate JWT
   │
   ▼
Create ClaimsPrincipal
   │
   ▼
Policy Evaluation
   │
   ▼
Membership = Premium ?
      │
 Yes  │ No
      │
      ▼
Controller Executes

Notice that policy evaluation happens after authentication.

If the user is not authenticated, the policy is never evaluated.

Practical Example

Let's continue with the SecureShop API.

Suppose two customers log in.

Customer 1

Name : John
 Membership : Premium

Customer 2

Name : Alex
 Membership : Standard

Now both users request:

GET /api/premium-products

The endpoint is protected using:

[Authorize(Policy = "PremiumOnly")]

John's Request

John is authenticated.

Membership claim = Premium.

Policy requirement satisfied.

API returns:

200 OK

Alex's Request

Alex is authenticated.

Membership claim = Standard.

Policy requirement fails.

API returns:

403 Forbidden

Although Alex is authenticated, he doesn't meet the policy requirements.

Combining Roles and Policies

ASP.NET Core allows both approaches to work together.

For example:

[Authorize(Roles = "Admin", Policy = "PremiumOnly")]

In this case, the user must:

  • Be an Admin

  • Have Premium membership

Both conditions must be satisfied.

This makes authorization very powerful for complex business scenarios.

When Should You Use Policies?

Policies are ideal when authorization depends on business rules rather than user categories.

Examples include:

  • Premium membership

  • Subscription plans

  • Department name

  • Employee type

  • Country or region

  • License status

  • Account verification

These rules can change over time without creating additional roles.

Common Mistakes

Mistake 1: Creating Roles for Everything

Roles should represent broad categories such as:

  • Admin

  • Customer

  • Manager

Avoid creating dozens of roles for business-specific conditions.

Use policies instead.

Mistake 2: Hardcoding Business Rules Inside Controllers

Some developers write authorization checks directly inside action methods.

For example:

if(User.FindFirst("Membership")?.Value != "Premium")
{
    return Forbid();
}

This duplicates logic across controllers.

Policies centralize authorization rules and make them reusable.

Mistake 3: Forgetting Required Claims

A policy that requires a claim will fail if that claim isn't included in the JWT.

Always ensure the token contains the claims your policies depend on.

Mistake 4: Confusing Roles with Claims

A role is just one type of claim.

Policies can evaluate any claim—not only roles.

This makes policies much more flexible.

Key Takeaways

  • Policy-Based Authorization provides more flexibility than Role-Based Authorization.

  • Policies evaluate business rules using claims, roles, or other requirements.

  • Register policies in Program.cs using AddAuthorization().

  • Apply policies with [Authorize(Policy = "...")].

  • Policies are ideal for requirements such as membership levels, departments, or subscription plans.

  • Use roles for broad user categories and policies for business-specific access rules.

Our SecureShop API can now authenticate users, authorize them by roles, and enforce business rules using policies. In the next article, we'll take authorization one step further by creating a Custom Authorization Requirement and Authorization Handler, allowing us to implement authorization rules that cannot be expressed using built-in role or claim checks alone.