Authorization in ASP.NET Core determines whether a user can access a particular route, controller, controller action, Resource, etc. This tutorial & subsequent tutorials covers the basics of Authorization, how it works & how to use it in ASP.NET Core.
Table of Contents
Authorization is the process, which determines what the user can do or cannot do.
It is different from Authentication, which determines who the user is.
Authentication always comes first, before Authorization. You are authorized only if you are Authenticated.
Authentication Recap
Authorization depends on Authentication to identify the user.
In ASP.NET Core we add the Authentication to Middleware pipeline using the UseAuthentication
method.
1 2 3 4 5 6 7 8 9 10 11 | app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); |
We usually add it after the UseRouting
, so that the authentication middleware knows about the URL being accessed by the User.
The purpose of the Authentication Middleware is to update the HttpContext.User
Property with the information about the user ( ClaimsPrincipal
). The ClaimsPrincipal
contains the claims
of the users.
All the middleware, which needs to know who the User
is (For Example Authorization middleware) must come after the UseAuthentication
You can read more about how Authentication works in ASP.NET Core.
The Authorization Middleware reads the ClaimsPrincipal
from HttpContext.User
and uses it to check whether the user is authorized.
We can use Authorization services in several ways to build a robust authorization system.
In this mode, we use the Authorize
attribute to secure a page. The Authorize
attribute describes how the page needs to be secured. The Authorization Middleware will read the attribute and figures out how to secure the page.
You can configure the Declarative Authorization using the following ways
- Simple Authorization
- Claim based Authorization
- Role based Authorization
- Policy based Authorization
Simple Authorization
Simple Authorization, we apply [Authorize]
attribute to a controller, action, or Razor Page. This will stop the unauthenticated users from accessing the page.
1 2 3 4 5 6 7 8 9 10 11 12 13 | [Authorize] public class HomeController : Controller { public ActionResult Login() { } public ActionResult Logout() { } } |
Claim based Authorization
In a claim based Authorization strategy, we build an Authorization Policy and map it to a claim (or claims) in the ConfigureServices
method of the startup class.
1 2 3 4 5 6 | services.AddAuthorization(options => { options.AddPolicy("AdminOnly", policy => policy.RequireClaim("Admin")); }); |
We apply the policy to a controller, action, or Razor Page using the Authorize
attribute. This will not only stop the unauthenticated users but also the authenticated users if they do carry the Admin
claim.
1 2 3 4 5 6 7 8 9 10 11 12 13 | [Authorize(Policy = "AdminOnly")] public class AdminController : Controller { public ActionResult Login() { } public ActionResult Logout() { } } |
Note that Claims cannot be used directly using the Authorize
attribute. You need to build a policy to use it.
Role based Authorization
The Role-based Authorization is very similar to the Claim-based Authorization, except for it uses the Roles instead of Claims.
Another difference is that you can use the Roles directly using the Authorize
attribute.
1 2 3 4 5 6 | [Authorize(Roles = "Admin")] public class AdminController : Controller { } |
You can also create a Policy using the RequireRole
in the startup class. and use the policy instead of Roles in the Controller.
1 2 3 4 5 6 7 | services.AddAuthorization(options => { options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin")); }); |
1 2 3 4 5 6 | [Authorize(Policy = "Admin")] public class AdminController : Controller { } |
Role-based authorization exists because of backward compatibility. It is better to avoid using it as it may be deprecated in the near future.
Policy based Authorization
Both claim-based & Role-based authorizations use the Policy behind the scenes. But if we run into complex situations, then we need to build a custom policy-based authorization scheme.
The building blocks of Policy authorization is policy, requirement & requirement handler.
The requirement
defines the authorization requirement.
The requirement handler
contains the logic that checks the requirement. A requirement can have more than one handler.
A Policy can contain more than one requirement.
Resource-based authorization Using Policies
The Authorization middleware, which uses the Authorize
attribute to check for permissions runs it much before the execution of the page handler or the action method. Hence it does not have the access to the data or resource on which the page or action method operates.
For Example, take a document that has an author. Only the Author can edit the document. The other users can only view the document. To implement the above security, we need to retrieve the document from the server, check its author and then decide whether to allow edit or not. The declarative authorization with Authorize
attribute cannot handle this situation.
Hence, we write the code to validate the user right in the method itself by injecting the Authorization service into the controller/ page. Then we use the AuthorizeAsync
method to manually trigger an authorization.
How it works
- The UseRouting resolves the parses incoming HTTP requests and constructs an Endpoint.
- Authentication Middleware constructs the ClaimsPrincipal from the Cookies or JWT Token and updates the
HttpContext.User
- The request reaches the Authorization Middleware. It checks whether to allow access to URL to the user
- If authorization is successful, the request proceeds normally to the endpoint middleware and completes
- But, if authorization fails, the middleware returns an error
If authorization fails, then there are two possibilities. Either the user is not authenticated or he is authenticated but not authorized.
The User is not authenticated
If the user is not authenticated, the Authorization middleware invokes the ChallengeResult
on the Authentication handler.
In a web application, the handler will redirect the user to the login page. In an API application, it returns a 401 Unauthorized error response.
If the user is authenticated but not authorized, the Authorization middleware invokes the ForbidResult
on the Authentication handler.
In a web application, the handler will redirect the user to the Access Denied web page. In an API application, it returns a 403 Forbidden error response
Reference
Read More
- ASP.NET Core Tutorial
- Authentication in ASP.NET Core
- Cookie Authentication in ASP.NET Core
- Introduction to ASP.NET Core Identity
- ASP.NET Core Identity Tutorial From Scratch
- Sending Email Confirmation in ASP.NET Core
- Add Custom Fields to the user in ASP.NET Core Identity
- Change Primary key in ASP.NET Core Identity
- JWT Authentication in ASP.NET Core
- Introduction to Authorization
- Simple Authorization using Authorize attribute
- Adding & Managing Claims in ASP.NET Core Identity
- Claim Based Authorization in ASP.NET Core
- Policy-based Authorization
- Resource-Based Authorization