Role-based authorization in ASP.NET Core
When an identity is created it may belong to one or more roles. For example, Tracy may belong to the Administrator
and User
roles while Scott may only belong to the User
role. How these roles are created and managed depends on the backing store of the authorization process. Roles are exposed to the developer through the IsInRole method on the ClaimsPrincipal class. AddRoles must be added to Role services.
While roles are claims, not all claims are roles. Depending on the identity issuer a role may be a collection of users that may apply claims for group members, as well as an actual claim on an identity. However, claims are meant to be information about an individual user. Using roles to add claims to a user can confuse the boundary between the user and their individual claims. This confusion is why the SPA templates are not designed around roles. In addition, for organizations migrating from an on-premises legacy system the proliferation of roles over the years can mean a role claim may be too large to be contained within a token usable by SPAs. To secure SPAs, see Use Identity to secure a Web API backend for SPAs.
Add Role services to Identity
Register role-based authorization services in Program.cs
by calling AddRoles with the role type in the app's Identity configuration. The role type in the following example is IdentityRole
:
builder.Services.AddDefaultIdentity<IdentityUser>( ... )
.AddRoles<IdentityRole>()
...
The preceding code requires the Microsoft.AspNetCore.Identity.UI package and a using
directive for Microsoft.AspNetCore.Identity
.
Adding role checks
Role based authorization checks:
- Are declarative and specify roles which the current user must be a member of to access the requested resource.
- Are applied to Razor Pages, controllers, or actions within a controller.
- Can not be applied at the Razor Page handler level, they must be applied to the Page.
For example, the following code limits access to any actions on the AdministrationController
to users who are a member of the Administrator
role:
[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
public IActionResult Index() =>
Content("Administrator");
}
Multiple roles can be specified as a comma separated list:
[Authorize(Roles = "HRManager,Finance")]
public class SalaryController : Controller
{
public IActionResult Payslip() =>
Content("HRManager || Finance");
}
The SalaryController
is only accessible by users who are members of the HRManager
role or the Finance
role.
When multiple attributes are applied, an accessing user must be a member of all the roles specified. The following sample requires that a user must be a member of both the PowerUser
and ControlPanelUser
role:
[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
public class ControlPanelController : Controller
{
public IActionResult Index() =>
Content("PowerUser && ControlPanelUser");
}
Access to an action can be limited by applying additional role authorization attributes at the action level:
[Authorize(Roles = "Administrator, PowerUser")]
public class ControlAllPanelController : Controller
{
public IActionResult SetTime() =>
Content("Administrator || PowerUser");
[Authorize(Roles = "Administrator")]
public IActionResult ShutDown() =>
Content("Administrator only");
}
In the preceding ControlAllPanelController
controller:
- Members of the
Administrator
role or thePowerUser
role can access the controller and theSetTime
action. - Only members of the
Administrator
role can access theShutDown
action.
A controller can be secured but allow anonymous, unauthenticated access to individual actions:
[Authorize]
public class Control3PanelController : Controller
{
public IActionResult SetTime() =>
Content("[Authorize]");
[AllowAnonymous]
public IActionResult Login() =>
Content("[AllowAnonymous]");
}
For Razor Pages, [Authorize]
can be applied by either:
- Using a convention, or
- Applying the
[Authorize]
to thePageModel
instance:
[Authorize(Policy = "RequireAdministratorRole")]
public class UpdateModel : PageModel
{
public IActionResult OnPost() =>
Content("OnPost RequireAdministratorRole");
}
Important
Filter attributes, including AuthorizeAttribute
, can only be applied to PageModel and cannot be applied to specific page handler methods.
Policy based role checks
Role requirements can also be expressed using the Policy syntax, where a developer registers a policy at application startup as part of the Authorization service configuration. This typically occurs in the Program.cs
file:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Administrator"));
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.Run();
Policies are applied using the Policy property on the [Authorize]
attribute:
[Authorize(Policy = "RequireAdministratorRole")]
public IActionResult Shutdown()
{
return View();
}
To specify multiple allowed roles in a requirement, specify them as parameters to the RequireRole method:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ElevatedRights", policy =>
policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));
});
var app = builder.Build();
The preceding code authorizes users who belong to the Administrator
, PowerUser
or BackupAdministrator
roles.
When an identity is created it may belong to one or more roles. For example, Tracy may belong to the Administrator and User roles whilst Scott may only belong to the User role. How these roles are created and managed depends on the backing store of the authorization process. Roles are exposed to the developer through the IsInRole method on the ClaimsPrincipal class.
We recommend not using Roles as claims, but rather using a claims. When using Single Page Apps (SPAs), see Use Identity to secure a Web API backend for SPAs.
Adding role checks
Role based authorization checks:
- Are declarative.
- Are applied to Razor Pages, controllers, or actions within a controller.
- Can not be applied at the Razor Page handler level, they must be applied to the Page.
Role-based authorization checks specify which roles which the current user must be a member of to access the requested resource.
For example, the following code limits access to any actions on the AdministrationController
to users who are a member of the Administrator
role:
[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
public IActionResult Index() =>
Content("Administrator");
}
Multiple roles can be specified as a comma separated list:
[Authorize(Roles = "HRManager,Finance")]
public class SalaryController : Controller
{
public IActionResult Payslip() =>
Content("HRManager || Finance");
}
The controller SalaryController
is only accessible by users who are members of the HRManager
role or the Finance
role.
If you apply multiple attributes then an accessing user must be a member of all the roles specified. The following sample requires that a user must be a member of both the PowerUser
and ControlPanelUser
role:
[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
public class ControlPanelController : Controller
{
public IActionResult Index() =>
Content("PowerUser && ControlPanelUser");
}
You can further limit access by applying additional role authorization attributes at the action level:
[Authorize(Roles = "Administrator, PowerUser")]
public class ControlAllPanelController : Controller
{
public IActionResult SetTime() =>
Content("Administrator || PowerUser");
[Authorize(Roles = "Administrator")]
public IActionResult ShutDown() =>
Content("Administrator only");
}
If multiple attributes are applied at the controller and action levels, all attributes must pass before access is granted:
[Authorize(Roles = "Administrator")]
public class ControlAllPanelController2 : Controller
{
public IActionResult SetTime() =>
Content("Administrator only");
[Authorize(Roles = "PowerUser")]
public IActionResult ShutDown() =>
Content("Administrator && PowerUser");
}
In the preceding ControlAllPanelController
controller:
- Members of the
Administrator
role or thePowerUser
role can access the controller and theSetTime
action. - Only members of the
Administrator
role can access theShutDown
action.
You can also lock down a controller but allow anonymous, unauthenticated access to individual actions.
[Authorize]
public class Control3PanelController : Controller
{
public IActionResult SetTime() =>
Content("[Authorize]");
[AllowAnonymous]
public IActionResult Login() =>
Content("[AllowAnonymous]");
}
For Razor Pages, the [Authorize]
can be applied by either:
- Using a convention, or
- Applying the
[Authorize]
to thePageModel
instance:
[Authorize(Policy = "RequireAdministratorRole")]
public class UpdateModel : PageModel
{
public ActionResult OnPost()
{
}
}
Important
Filter attributes, including AuthorizeAttribute
, can only be applied to PageModel and cannot be applied to specific page handler methods.
Policy based role checks
Role requirements can also be expressed using the new Policy syntax, where a developer registers a policy at startup as part of the Authorization service configuration. This normally occurs in ConfigureServices()
in your Startup.cs
file.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Administrator"));
});
}
Policies are applied using the Policy
property on the [Authorize]
attribute:
[Authorize(Policy = "RequireAdministratorRole")]
public IActionResult Shutdown()
{
return View();
}
If you want to specify multiple allowed roles in a requirement then you can specify them as parameters to the RequireRole
method:
options.AddPolicy("ElevatedRights", policy =>
policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));
This example authorizes users who belong to the Administrator
, PowerUser
or BackupAdministrator
roles.
Add Role services to Identity
Append AddRoles to add Role services:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
}
ASP.NET Core