🏰 The Castle of Authorization: Who Gets In?
Imagine you have a magical castle with many rooms. Some rooms are for everyone, some are only for knights, and some are only for the king! Authorization is the guard at each door, deciding who can enter.
Authentication = “Who are you?” (showing your ID) Authorization = “What can you do?” (checking if you’re allowed in that room)
🎯 Authorization Overview
Think of authorization like a theme park:
- You bought a ticket (authentication - you proved who you are)
- But can you ride the VIP roller coaster? That depends on your wristband type (authorization)!
graph TD A["User Arrives"] --> B{Authenticated?} B -->|No| C["Go Login!"] B -->|Yes| D{Authorized?} D -->|No| E["Access Denied 🚫"] D -->|Yes| F["Welcome In! ✅"]
In ASP.NET, authorization happens AFTER authentication. First, we know who you are. Then, we check what you can do.
🔓 Simple Authorization
The easiest type! Like a door that only opens if you have ANY valid ticket.
The [Authorize] Attribute
Just add [Authorize] and boom - only logged-in users can enter!
[Authorize]
public class SecretController
: Controller
{
public IActionResult Index()
{
// Only logged-in users
// can see this!
return View();
}
}
Allow Anonymous
Want to let EVERYONE see one page? Use [AllowAnonymous]:
[Authorize]
public class ProfileController
: Controller
{
[AllowAnonymous]
public IActionResult PublicInfo()
{
// Anyone can see this!
return View();
}
public IActionResult PrivateInfo()
{
// Must be logged in
return View();
}
}
🎈 Simple Example:
- The school cafeteria is
[Authorize]- only students allowed - The school entrance has
[AllowAnonymous]- visitors can come in too!
👑 Role-Based Authorization
Like different colored wristbands at a water park:
- 🟢 Green = Can use regular slides
- 🟡 Yellow = Can use big slides
- 🔴 Red (VIP) = Can use ALL slides!
[Authorize(Roles = "Admin")]
public class AdminController
: Controller
{
// Only Admins can enter!
public IActionResult Dashboard()
{
return View();
}
}
Multiple Roles (OR Logic)
Any of these roles can enter:
[Authorize(Roles = "Admin,Manager")]
public IActionResult Reports()
{
// Admin OR Manager can see
return View();
}
Require ALL Roles (AND Logic)
Stack the attributes - must have BOTH:
[Authorize(Roles = "Admin")]
[Authorize(Roles = "SuperUser")]
public IActionResult TopSecret()
{
// Must be Admin AND SuperUser!
return View();
}
graph TD A["User Request"] --> B{Has Role?} B -->|Admin| C["✅ Enter Admin Area"] B -->|Manager| D["✅ Enter Reports"] B -->|Guest| E["🚫 Access Denied"]
🏷️ Authorization Attributes
These are like sticky labels you put on doors to say who can enter!
Where Can You Put Them?
| Level | What It Does |
|---|---|
| Controller | Protects ALL actions inside |
| Action | Protects just ONE method |
| Global | Protects the WHOLE app |
Controller Level
[Authorize] // Protects everything!
public class AccountController
: Controller
{
// All methods here need login
}
Action Level
public class HomeController
: Controller
{
public IActionResult Index()
{
return View(); // Anyone!
}
[Authorize]
public IActionResult Profile()
{
return View(); // Login needed!
}
}
Global Level (in Program.cs)
builder.Services
.AddAuthorization(options =>
{
options.FallbackPolicy =
new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
Now EVERYTHING needs login by default!
📜 Claims-Based Authorization
Claims are like facts written on your ID card:
- Name: “Alice”
- Age: 25
- Department: “Engineering”
- HasDrivingLicense: true
You can check these facts to allow access!
// In Program.cs
builder.Services
.AddAuthorization(options =>
{
options.AddPolicy("CanDrive",
policy => policy
.RequireClaim("DrivingLicense"));
});
[Authorize(Policy = "CanDrive")]
public IActionResult RentCar()
{
// Only if you have
// a DrivingLicense claim!
return View();
}
Claim with Specific Value
options.AddPolicy("Over18",
policy => policy
.RequireClaim("Age", "18", "19",
"20", "21")); // or higher
🎈 Real Life Example:
- Claim: “EmployeeType” = “FullTime”
- Only full-time employees can access the bonus calculator!
📋 Policy-Based Authorization
Policies are like rulebooks - combine multiple requirements into one name!
Creating a Policy
builder.Services
.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly",
policy => policy
.RequireRole("Admin")
.RequireClaim("Department",
"IT"));
});
Using a Policy
[Authorize(Policy = "AdminOnly")]
public IActionResult ServerSettings()
{
// Must be Admin role
// AND IT department!
return View();
}
Built-in Requirements
| Requirement | What It Checks |
|---|---|
RequireRole() |
User has this role |
RequireClaim() |
User has this claim |
RequireUserName() |
Specific username |
RequireAssertion() |
Custom true/false check |
Custom Assertion
options.AddPolicy("WorkHours",
policy => policy
.RequireAssertion(context =>
{
var hour = DateTime.Now.Hour;
return hour >= 9 && hour <= 17;
}));
This only allows access between 9 AM and 5 PM! 🕐
🛠️ Custom Authorization Handlers
When the built-in checks aren’t enough, build your own!
Step 1: Create a Requirement
public class MinimumAgeRequirement
: IAuthorizationRequirement
{
public int MinAge { get; }
public MinimumAgeRequirement(
int minAge)
{
MinAge = minAge;
}
}
Step 2: Create a Handler
public class MinimumAgeHandler
: AuthorizationHandler
<MinimumAgeRequirement>
{
protected override Task
HandleRequirementAsync(
AuthorizationHandlerContext ctx,
MinimumAgeRequirement req)
{
var dob = ctx.User
.FindFirst("DateOfBirth");
if (dob != null)
{
var birthDate =
DateTime.Parse(dob.Value);
var age = DateTime.Today.Year
- birthDate.Year;
if (age >= req.MinAge)
{
ctx.Succeed(req);
}
}
return Task.CompletedTask;
}
}
Step 3: Register Everything
// In Program.cs
builder.Services
.AddSingleton
<IAuthorizationHandler,
MinimumAgeHandler>();
builder.Services
.AddAuthorization(options =>
{
options.AddPolicy("AtLeast21",
policy => policy.Requirements
.Add(new MinimumAgeRequirement(21)));
});
Step 4: Use It!
[Authorize(Policy = "AtLeast21")]
public IActionResult BuyBeer()
{
return View();
}
graph TD A["Request Arrives"] --> B["Find Handler"] B --> C["Handler Checks Age"] C -->|Age >= 21| D["ctx.Succeed ✅"] C -->|Age < 21| E["Access Denied 🚫"]
🎁 Resource-Based Authorization
Sometimes you need to check: “Can THIS user edit THIS specific document?”
It’s not just about WHO you are, but WHAT you’re trying to access!
The Service
public class DocumentService
{
private readonly
IAuthorizationService _auth;
public DocumentService(
IAuthorizationService auth)
{
_auth = auth;
}
public async Task<IActionResult>
Edit(Document doc,
ClaimsPrincipal user)
{
var result = await _auth
.AuthorizeAsync(user, doc,
"EditDocument");
if (result.Succeeded)
{
// Can edit!
}
else
{
// Cannot edit!
}
}
}
The Requirement
public class DocumentOwnerRequirement
: IAuthorizationRequirement
{ }
The Handler
public class DocumentOwnerHandler
: AuthorizationHandler
<DocumentOwnerRequirement, Document>
{
protected override Task
HandleRequirementAsync(
AuthorizationHandlerContext ctx,
DocumentOwnerRequirement req,
Document doc)
{
var userId = ctx.User
.FindFirst(ClaimTypes.NameIdentifier)
?.Value;
if (doc.OwnerId == userId)
{
ctx.Succeed(req);
}
return Task.CompletedTask;
}
}
🎈 Real Life Example:
- Alice can edit HER documents
- Bob can edit HIS documents
- Neither can edit each other’s!
🎨 Summary: The Authorization Family
| Type | Best For | Example |
|---|---|---|
| Simple | “Any logged-in user” | [Authorize] |
| Role-based | User groups | Admin, Manager |
| Claims-based | User facts/properties | Age, Department |
| Policy-based | Combined rules | Admin + IT Department |
| Custom Handlers | Complex logic | Age calculation |
| Resource-based | Per-item access | “Your own documents” |
🚀 Quick Tips
- Start Simple - Use
[Authorize]first, add complexity when needed - Policies are Reusable - Define once, use everywhere
- Combine Wisely - Role + Claims = Powerful policies
- Think Resources - Sometimes it’s about WHAT, not just WHO
- Handler = Custom Logic - When built-ins aren’t enough
🏆 You did it! Now you know how to guard your castle with ASP.NET Authorization. From simple locks to complex guard systems, you have all the tools you need!
graph TD A["You!"] --> B["Simple Auth"] B --> C["Roles"] C --> D["Claims"] D --> E["Policies"] E --> F["Custom Handlers"] F --> G["Resource-Based"] G --> H["🏆 Master!"]
