This post is part of a series on ASP.NET Core 2.0 Authentication and I am going to talk about authorization policies.

Series

Application

Application is based on previous post, ASP.NET Core 2.0 Authentication with local logins – Implementing claims transformation, with a few additions.
This time, I am going to demonstrate how to enforce authorization policies on pages, on Razor MVC views, and finally mixing authorization policies in order to decide if user should have access to the resource or not.

Aside from the familiar home, login and profile pages, I have added a few more. Namely, I have added a page that has an age requirement, so in order to have access to this page, a user must be older than the minimum age requirement. This example, demonstrates simply how to utilize a single authorization policy and enforce it to a specific page.

To be able to demonstrate how to use authorization policies in a Razor MVC View, I have also added a new page, which is going to be visible in the navbar only when the user meets its requirements.

Finally, I have also added a couple more pages to support a more complex example of using and mixing authorization policies. If you are familiar with meetups, you already know that a meetup is a user group which contains various members and some of its members are special, as they are founders or co-founders of the group, meaning they spend time to organize talks, foods, events and many other stuff.
In order for someone to be able to have access to a meetup, he should be a member, like I mentioned earlier. If that user is not a member, then he is denied access to that meetup. To make it a little more complex, user should be rsvp’d for that meetup as well, in order to view the attendee list.
In this application, I have added a new page, which lists all meetups. If the user tries to access a meetup, the authorization policy will kick in, and based on user’s role and his rsvp status, it will either grant or deny access.

Source code

You can find the code outlined in this article on Github.

Authorization Policies

Authorization is a step that comes after authentication. I usually think about those terms in the following way:

  • Authentication is the process that wants me to verify who I am.
    • Who are you?
  • Authorization is the process that wants me to verify if I am qualified to access a certain resource based on predefined policies.
    • Are you allowed to perform this action?

Usually we perform authorization based on user’s claims. These claims might be in a cookie or in a JWT token or in a SAML token, it doesn’t really matter what the transport/store type is, all these have something in common and that is that they carry a specified amount of claims associated with a user.

Based on those claims now, we as developers, have the ability to create features which are very precise and solve much more complex authorization requirements. In ASP.NET Core, this ability is granted by authorization policies.

An authorization policy comprises of three components:

  1. The requirement
  2. The handler
  3. The configured policy

Requirements

A requirement defines the business logic behind an authorization policy. It can be used by one or more policies and in reality it is just a plain class object which implements the empty IAuthorizationRequirement interface. You can define your own parameters, properties and data for that requirement, which will be used directly by a handler.
A requirement can have multiple handlers.

Handlers

A handler evaluates the requirement’s properties/business logic against its context to determine if access is allowed or not, so it can delegate the process to another handler or terminate it on the spot. You can use a handler to work with a specific requirement, and that handler should inherit from AuthorizationHandler<TRequirement> abstract class. You’ll need to override the HandleRequirementAsync method, which has the following definition:

Makes a decision if authorization is allowed based on a specific requirement.

From this we can understand that a handler plays a crucial role in deciding to provide access or not to a user. This method, except of the AuthorizationHandlerContext context that is passed, it takes a generic TRequirement, which is the requirement provided in the AuthorizationHandler generic parameter.

A handler can have two states

  1. Succeeded
  2. Failed

A success state means after validating against the requirement, user’s claims were valid, thus, access is granted, based on that policy. In order to fulfill a requirement, a simple call to the AuthorizationHandlerContext.Succeed method must commence, passing the requirement as a parameter. More on this in code coming next.

The failed state is more complicated. As mentioned earlier, a call to the Succeed method is enough to fulfill a given requirement, but doing nothing essentially means the requirement failed. Though, if we look at the AuthorizationHandlerContext, a Fail method exists. Well, even if it exists it should be used with caution and I will explain why shortly.

Imagine this, fairly often we have several handlers responsible to verify a requirement. As long as one of them calls the Succeed method, access is approved.
But, if any of them fails, thus calling the Fail method, access is denied, no matter how may of the other handlers succeeded. If nobody calls the Succeed method, then the requirement is going to be fail, but this won’t stop the process from moving to the next handler for that requirement.
That said, we can understand that leaving out the call to the Fail method, gives us the ability to introduce additional handlers in the future, without needing to alter any existing handler code.

Fail method should only be used if there is some form of absolute blocker that would render the rest of the handlers and essentially rest of the policy useless, so use this method with caution.

Configuring a policy

To setup an authorization policy, you just need to use the authorization middleware, which can be registered in the pipeline by the AddAuthorization method in ConfigureServices. You can register as many policies as you like, but remember to map them to the appropriate authentication scheme if needed.
If you have already set the DefaultPolicy to have one default authentication scheme, then all the other policies will derive from it.

Use the AddPolicy method to add new policies by name, which can be either delegates or classes (OOP approach).

Then you can use that name with the Authorize attribute, for instance, to provide sophisticated security over a resource.

Implementation

Let’s start off with the implementation, first we need to add the authorization middleware into the pipeline, so in ConfigureServices call the AddAuthorization method. I will provide the default authentication scheme to be used for all policies to be CookieAuthenticationDefaults.AuthenticationScheme.

This sets up a default authorization policy, which requires the user to be authenticated. The default authorization scheme for this policy and for the next to come is "cookie", for cookie authorization.

Age Requirement

I want to create a new page where people under 18 would be denied access. Let’s first create and secure this page. In HomeController I’m adding the following action.

I’ve decorated this action with the Authorize attribute, defining a policy. Now, when a user navigates to /protected route, authentication will kick in first, to determine if that user is indeed authenticated and if that is true, the authorization policy will run to determine if access should be granted. If user is denied access, the default access denied screen will show, else resource will be presented to the user.
The Policies.AgeRestriction is just a constant with the value "AgeRestriction".
Configuration is fetched from appsettings.json file, following is some part of that config file

Now that we have everything in place, it’s time to add that policy to the list of authorization policies. To do that, I need to get back the Startup.cs class to ConfigureServices. In AddAuthorization method we saw earlier, I will use the AuthorizationOptions parameter to call the AddPolicy method and register a new policy.

I mentioned earlier, that in order to make a new policy you need three things, a requirement, a handler and to configure the policy. I’ve done the latter, now I need the requirement and its handler.
I could do them separately, but I have the ability to combine them in one class, by inhering from AuthorizationRequirement<TRequirement> and implementing the IAuthorizationRequirement interface.
That said, I created a single class and I named it AgeRestrictionHandler, and got to inherit and implement from the aforementioned objects.

Based on the above, we have a handler that itself is the requirement, passing the actual requirement in the constructor, which is the minimum age.
In HandleRequirementAsync, I am testing against that requirement, by first fetching the date of birth claim. Notice that if the dateOfBirth is non-existent (null), the method returns, which means the policy has failed, but this is a soft fail, compared to a hard by calling the Fail method. If the test against the age is successful, then a call to the Succeed method commences. Notice that you need to pass the requirement along with this method.

View based authorization

There are sometimes that you want to hide specific UI elements. You might want these elements to be visible to certain users, ones that have the appropriate claims or fulfill a certain authorization policy. Of course, this is not a way to secure an application, rather its a way  to provide a better UX for your users.

In order to hide and show elements in Razor based on authorization policies, you need an instance of the IAuthorizationService in your views. You can either inject that service in your view or inject it _ViewImports.cshtml to make it globally accessible. In this demo, I’ve injected it in _ViewImports

Now, I want to show/hide a menu item in _Layout.cshtml, based on a policy. I will use that IAuthorizationService and call its AuthorizeAsync method. This will tell me if the authorization is successful or failed for that user. If it is successful, then I will be able to show the menu item, else it stays hidden.

Checks if a user meets a specific authorization policy against the specified resource.

Returns a flag indicating whether policy evaluation has succeeded or failed. This value is true when the user fulfills the policy, otherwise false.

This method is asynchronous, so be sure to await it. The returned value is of type AuthorizationResult, which has two public properties

It requires a ClaimsPrincipal user object and has various overloads for the rest of its parameters, though in my case, I prefer the overload that contains a policy name string as a third parameter. This overload validates the user against the specified policy.

Next step is to create that policy, so firstly, I will add that authorization policy in ConfigureServices.

The Policies.DomainRestriction is a constant with a value of "DomainRestriction".
I also fetch some configuration data from Policies:Domains section in appsettings.json.

This lists the allowed domains which are passed as constructor argument for the requirement.
Like in the previous example, I created a class which is both a handler and a requirement.
This requirement allows access only to users with email addresses which are in a specific domain, for example, this email address myemail@customDomain.com is not valid as the domain is not in the list of the allowed domains we saw earlier.

This handler succeeds only when user’s email address domain is listed in the allowed domains.

Complex authorization policies

Finally, I will talk about setting up complex authorization policies. You might want to use multiple handlers on a requirement, like in this example, I want to be able to allow or deny access to a user based on his role (Member, Founder, Co-founder) and his RSVP status. I would like to evaluate role requirements in an OR basis, whereas the RSVP status in an AND basis. If the user is not RSVP’ed, then the policy fails.
I’d like to start from setting the requirements first, then I will go to handlers and finally, I will show the configuration.

I start first by defining a MeetupAccessRequirement, which is an empty marker class that implements the IAuthorizationRequirement. Reason behind that is that I want to add a single requirement and have multiple handlers use that requirement, but I am not interested particularly in specific data or.

Now, I will create a handler for each role (Member, Founder, Co-founder), in order to have independent business logic for each one. That is the recommended best practice, it is better to have handlers that conform with the single responsibility principle, doing one thing, like in this case different handlers for each role and for RSVP status.

Then comes the handler for the founder

And finally, the handler for the co-founder role

All these are pretty much similar and simple handlers, they just verify if the user is in a role.
Next up, is the RSVP handler, which checks if a user is RSVP’ed to the meetup. This is a more complex handler.

I inject a service in this handler in order to be able to communicate with the data store.
Notice in HandleRequirementAsync method, that I cast the Resource property of AuthorizationHandlerContext to an AuthorizationFilterContext. The MVC framework populates this property with an instance of AuthorizationFilterContext, which automatically gives us access to all MVC items, like RouteData, HttpContext, etc.
It’s proven very useful for me, because I want the id route data, which is the meetup Id.
In order to verify if the user is RSVP’ed, I call the FindAttendeeInMeetupAsync, passing the meetup Id and user’s username. If user has indeed RSVP’ed, the policy is successful, otherwise, the policy fails hard, preventing further handlers from executing for this requirement.

For more info on services, repositories and database items, please look at the source code on Github.

Final piece in this puzzle, is the authorization policy configuration in ConfigureServices. I have to add that requirement in authorization options, but I also have to register all the Meetup* handlers in DI container, as an IAuthorizationHandler.

Securing actions remains the same as with previous examples, just use the Authorize attribute and specify the policy name to match the one registered.

Summary

In this post, we’ve gone through authorization policies in ASP.NET Core. We’ve seen what a requirement is, the handler’s role and how to stitch them all together.

We’ve gone through some examples, starting from defining a simple requirement, to view based authorization and finally to applying complex authorization policies.

Next up, I will talk about social logins.

About the Author George Dyrrachitis

👉 Software Engineer, Senior back-end developer, C# & ASP.NET expert, Angular enthusiast, clean coder, blogger 👈

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s