Sunday, July 30, 2023

ASP.NET Core Razor Pages - Authorization of Areas based on Roles

In previous article, we see the concept of Areas in ASP.NET Core Razor Pages. We learnt about creating areas and navigation to any page of any area. In this part, we will see how Authorization can be applied in Areas so that based on the roles of the users, access can be granted or denied for an area and its pages. When an user will try to navigate to any page of  unauthorized area, access will be denied. To achieve this goal, we have to make changes in the configuration of the application. As we know that configuration is done mostly in Startup class, we will update the Startup class as per that.

AddAuthorization method with overloaded options will be added as service in the IServiceCollection.

AuthorizationPolicy represents a collection of authorization requirements and the scheme or schemes they are evaluated against, all of which must succeed for authorization to succeed. AuthorizationOptions class helps to add or get authorization policy. A policy is name added to AuthorizationPolicy class. AuthenticationSchemes are names of schemes in string.

The AddRazorPages method will be overloaded with options. AddRazorPages supports razor pages in the app but using itsoverloaded version allows adding policy on the area folders. We can authorize an area folder with some role for authorization policy.

Authorization refers to the process that determines what a user is able to do. For example, an administrative user is allowed to create a document library, add documents, edit documents, and delete them. A non-administrative user working with the library is only authorized to read the documents.
Authorization is orthogonal and independent from authentication. However, authorization requires an authentication mechanism. Authentication is the process of ascertaining who a user is. Authentication may create one or more identities for the current user.
Authorization types
ASP.NET Core authorization provides a simple, declarative role and a rich policy-based model. 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.
Startup class

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace RPWebApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            
            services.AddAuthorization(config =>
            {
                // First parameter is policy name
                // Second parameter is action delegate to build policy 
                // with the required role.
                // More than one role can be assigned using param array 
                // RequireUserName, RequireClaim, RequireRole etc. can be used
                // for authentication.
                // Policy string name will be used elsewhere in the app
                // wherever policy is required. e.g. in services.AddRazorPages();
                config.AddPolicy("teacher_policy",
                    authPolicy => authPolicy.RequireRole("Teach"));
                config.AddPolicy("student_policy",
                    authPolicy => authPolicy.RequireRole("Study"));
            });

            services.AddRazorPages(opt =>
            {
                // conventions collection useful in routes
                // param1: areaname, param2: folderpath, param3: policy name
                opt.Conventions.AuthorizeAreaFolder("Teacher","/", "teacher_policy");
                opt.Conventions.AuthorizeAreaFolder("Student","/", "student_policy");
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoint =>
            {
                endpoint.MapRazorPages();
            });
        }
    }
}
Role based Authorization
First of all, we added two policies names in the authorization configuration option and in authorization policy builder we used the roles to build the authorization. We could have used some other things like claim, assertion, username etc. as well instead of role. The AuthorizationPolicyBuilder class is used for this. Second, we used Conventions property of RazorPagesOptions class to get PageConventionCollection class. This class is used to configure folders in Razor pages. The AuthorizeAreaFolder is an extension method of PageConventionCollection.

The authorization policy named teacher_policy will look for a role named Teach.Similarly, the authorization policy named student_policy will look for a role named Study. Already mentioned that in stead of role, username, claim or any other assertion can be used. Note that these authorization policy names are used in AuthorizeAreaFolder method.

Authentication Missing
On running the app and clicking the link to go to teacher page, we get exception as given below. 

This is because of authentication missing in the code. Although role based authorization is set, it must be authenticated when link is clicked. There are different authentication schemes such as cookies or tokens. We can implement any of them in the application.

Add Cookies authentication 
We use the cookies authentication in this app. So, we add the following lines of code in ConfigureServices.
 services.AddAuthentication("Cookies").AddCookie(
                configCookie =>
                {
                    configCookie.LoginPath = "/Login";
                    configCookie.AccessDeniedPath = "/AccessDenied";
                }
                );
Note. We should use CookieAuthenticationDefaults.AuthenticationScheme which returns Cookies in AddAuthentication method.
Run the application, we still on clicking the links get the error as given below.



The reason is that the Login page is missing in the app. Add Login page in the root Pages folder. Design login form in this login page and run the application again. We navigate to the login page when link is clicked.
Note that we have not used app.UseAuthentication(); in the Startup class. It is required when login form is submitted for authentication.

When user fills the name and password in the login form and clicks the submit button. The data is verified from the database. We have not created database. Let name be Ajeet and password be Appliedk. If both matches then user is redirected to a page which is desired by the user. This redirected page is called ReturnUrl.

After successful login, authentication ticket is generated which contains details such as Email, Password etc.

 
We should add UseCookiePolicy middleware in the Configure method before UseAuthentication as given below.

In the OnGet of Teacher IndexModel page, we must implement the logic to check it.


From Stack overflow. The way the authentication stack works in ASP.NET Core is that you can configure a set of authentication schemes. Some of these schemes are meant to be used in combination, for example the cookie authentication scheme is rarely used on its own, but there are also schemes that can be used completely separate (for example JWT Bearer authentication).

Authentication actions
In the authentication world, there are certain actions that you can perform:

Authenticate: To authenticate basically means to use the given information and attempt to authenticate the user with that information. So this will attempt to create a user identity and make it available for the framework.

For example, the cookie authentication scheme uses cookie data to restore the user identity. Or the JWT Bearer authentication scheme will use the token that is provided as part of the Authorization header in the request to create the user identity.

Challenge: When an authentication scheme is challenged, the scheme should prompt the user to authenticate themselves. This could for example mean that the user gets redirected to a login form, or that there will be a redirect to an external authentication provider.

Forbid: When an authentication scheme is forbidden, the scheme basically just responds with something that tells the user that they may not do whatever they attempted to do. This is commonly a HTTP 403 error, and may be a redirect to some error page.

Sign-in: When an authentication scheme is being signed in, then the scheme is being told to take an existing user (a ClaimsPrincipal) and to persist that in some way. For example, signing a user in on the cookie authentication scheme will basically create a cookie containing that user’s identity.

Sign-out: This is the inverse of sign-in and will basically tell the authentication scheme to remove that persistance. Signing out on the cookie scheme will effectively expire the cookie.

What is the difference between DefaultScheme and DefaultSignInScheme?
DefaultSignInScheme : Sets the default scheme to sign in. 
DefaultSignOutScheme : Sets the default scheme to sign out. 
DefaultScheme : Sets the default fallback scheme.

Read the Stack overflow link for all details. Also MSDN
For the user, the normal interaction is through the cookie authentication scheme: When they access the web application, the cookie authentication scheme will attempt to authenticate them using their cookie. So using the cookie authentication scheme as the default scheme for all operations.

No comments:

Post a Comment

Hot Topics