Role-Based Authorization
Authentication answers “who are you?”. Authorization answers “what may you do?”. Roles — Admin, Manager, Customer — are the simplest way to decide who can reach which endpoints.
On this page · 6 min · Intermediate
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Intermediate
- Updated
- Oct 28, 2026
Related tutorials
Learning objectives#
- Restrict controllers and actions with [Authorize(Roles = ...)]
- Understand how multiple Authorize attributes combine
- Read roles from claims and check User.IsInRole
- Return 403 Forbidden for signed-in users without the role
Explanation#
Roles are claims in the token (ClaimTypes.Role), added when the token was issued.
[Authorize(Roles = "Admin,Manager")] allows users in ANY of the listed roles. Stacking two [Authorize] attributes (controller + action) requires BOTH to pass.
Inside an action, User.IsInRole("Admin") lets you vary behaviour, such as showing cost prices only to admins.
Web API 2’s AuthorizeAttribute returns 401 both for anonymous users and for signed-in users without the role. Override HandleUnauthorizedRequest to return 403 for the second case, which clients can handle correctly.
Roles cannot express everything. “Users may only see their own orders” is a resource check in code: compare the order’s owner with the current user and return 403 or 404.
Syntax#
[Authorize(Roles = "Admin,Manager")] // Admin OR Manager
public class ReportsController : ApiController
{
[Authorize(Roles = "Admin")] // AND additionally Admin
public IHttpActionResult Delete(int id) { ... }
}
Code example: Admin endpoints and a 403-aware attribute#
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Web.Http;
using System.Web.Http.Controllers;
// Returns 403 (not 401) when the user is signed in but lacks the role
public class RoleAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
var principal = actionContext.RequestContext.Principal;
if (principal != null && principal.Identity.IsAuthenticated)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.Forbidden, "You do not have permission to do this.");
}
else
{
base.HandleUnauthorizedRequest(actionContext); // 401
}
}
}
[RoutePrefix("api/admin/products")]
[RoleAuthorize(Roles = "Admin,Manager")]
public class AdminProductsController : ApiController
{
[HttpGet, Route("")]
public IHttpActionResult GetAll()
{
bool showCost = User.IsInRole("Admin");
return Ok(new[] { new { Name = "Pen", Price = 20m, Cost = showCost ? (decimal?)12m : null } });
}
[HttpDelete, Route("{id:int}")]
[RoleAuthorize(Roles = "Admin")] // Manager is not enough here
public IHttpActionResult Delete(int id)
{
return StatusCode(HttpStatusCode.NoContent);
}
}
public class MeController : ApiController
{
[Authorize]
[HttpGet, Route("api/me")]
public IHttpActionResult Get()
{
var identity = (ClaimsIdentity)User.Identity;
var roles = identity.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToList();
return Ok(new { name = identity.Name, roles });
}
}
GET /api/admin/products (no token) → 401
GET /api/admin/products (Customer token) → 403 {"Message":"You do not have permission to do this."}
GET /api/admin/products (Manager token) → 200 [{"Name":"Pen","Price":20.0,"Cost":null}]
GET /api/admin/products (Admin token) → 200 [{"Name":"Pen","Price":20.0,"Cost":12.0}]
DELETE /api/admin/products/1 (Manager token) → 403
DELETE /api/admin/products/1 (Admin token) → 204
GET /api/me (Manager token) → 200 {"name":"ravi","roles":["Manager"]}This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- RoleAuthorizeAttribute inherits AuthorizeAttribute and only changes what happens on failure.
- The controller allows Admin or Manager; Delete adds a second attribute, so only Admin passes both.
- User.IsInRole("Admin") hides the cost price from managers without needing a separate endpoint.
- MeController reads all role claims from the ClaimsIdentity — useful for front ends deciding which menus to show.
Real-world use case#
A back-office API lets Support staff view orders, Managers issue refunds up to ₹5,000 (checked in the service), and only Admins delete products. Each rule is one attribute or one check, easy to audit.
Common mistakes#
Roles = "Admin, Manager" works because Web API trims, but a typo like "admin" vs "Admin" silently denies access. Use constants.
Hiding the Delete button in the UI does not stop someone calling DELETE directly. Authorize on the server.
A Customer role does not mean “any customer’s order”. Check that the order belongs to the current user.
Best practices#
- Define role names as constants
- Authorize at controller level, tighten per action
- Return 403 for authenticated-but-forbidden
- Combine roles with resource ownership checks
Remember this#
Roles = "A,B" → A OR B. Two attributes → BOTH.
401 = not signed in. 403 = signed in, not allowed.
Roles say what kind of user; ownership checks say which data.
Practice exercise#
Your task: Write GET api/orders/{id} that returns 404 if the order does not exist, 403 if it belongs to another user (unless the caller is Admin), and 200 otherwise. Orders are a static Dictionary<int, string> of id → owner user name.
Need a hint?
Show solution
using System.Collections.Generic;
using System.Net;
using System.Web.Http;
[Authorize]
public class OrdersController : ApiController
{
private static readonly Dictionary<int, string> Owners = new Dictionary<int, string> { [1] = "asha", [2] = "ravi" };
[HttpGet, Route("api/orders/{id:int}")]
public IHttpActionResult Get(int id)
{
string owner;
if (!Owners.TryGetValue(id, out owner)) return NotFound();
if (owner != User.Identity.Name && !User.IsInRole("Admin"))
return StatusCode(HttpStatusCode.Forbidden);
return Ok(new { id, owner });
}
}
Mini quiz#
What does [Authorize(Roles = "Admin,Manager")] allow?
Want more? Practise all ASP.NET Web API MCQs · ASP.NET Web API interview questions
My notes#
Saved in this browser.