←  Back

ASP.NET Core custom authorization attributes

When implementing an API using ASP.NET Core, there’s often a need to authorize that API’s users. Your system might be organized into several separate areas, which provide access to different resources and actions. It’s very likely that not all users are allowed to use all of those areas. Within a single area, a design might require restricted access to data entities themselves. There are many ways to implement such authorization, one of them being declaring custom authorization attributes on controller methods/actions or on controllers as a whole.

 

The permission model

 

Depending on the design of your application, it could be necessary to develop a permission model that’ll describe what the user is allowed to do in a specific area of the system. Let’s say, for example, that the API has two areas: the administrator area and the buyer area. The administrator of the API will be able to:

 

  • manage warehouses
  • manage items in the warehouses
  • manage the list of users who can buy from those warehouses.

 

The buyer of the API will be able to:

 

  • see the list of warehouses he or she is assigned to
  • see the list of products in a warehouse
  • buy products in a warehouse.

 

Once we’ve organized the API in this way, we can create a simple object that represents all the possible permissions. How this data is stored in the database can be implemented in different ways, but here we can just assume we’ve got a method to fetch the UserPermissions object for a given user Id.

 

ASP.NET Core custom authorization attributes

 

On every API method call which must be protected by our custom authorization, we have to acquire the relevant UserPermissions object. One option is to query the database and build the object on every API call, but that could cause performance issues. Another option is to create the object during user login and serialize it into the JWT token. So on each API call, the client-side will send the UserPermissions object as a part of the token, and it’ll be deserialized in the back-end custom authorization code. Handling the lifetime of the token and, therefore, the lifetime of the UserPermissions object is important in this case.

 

Custom authorization attributes

 

The main goal of this text is to build easy-to-use custom attributes that’ll allow us to easily define the requested permissions for API calls. So let’s create a new class: ApiAuthorizationFilter. It’ll be a derived class of ActionFilterAttribute, a class in the Microsoft.AspNetCore.Mvc.Filters namespace, used for executing a code before, or after, controller actions. For our custom authorization, we’ll override the OnActionExecuting method. The constructor will take a parameter which is a list of AdministratorPermissions (enum), representing all the permissions that allow a user to call the method to which the ApiAuthorizationFilter is applied to.

 

The GetUserPermissions method reads the token and deserializes it into a UserPermissions object.

 

ASP.NET Core custom authorization attributes

 

ASP.NET Core custom authorization attributes

 

Applying the custom attribute

 

The custom attribute is now ready to be applied to a controller action. It’s simply declared above the method, with the permissions required for that particular piece of code. And that’s all there is to it.

 

Before calling the controller action, the OnActionExecuting will be called. The constructor parameter can be used to search through the UserPermissions object provided through the token. In case the permission doesn’t exist, the action is forbidden to run.

 

ASP.NET Core custom authorization attributes

 

If the entire controller requires the same set of permissions, the ApiAuthorizationFilter attribute can be applied above the Controller definition. It’ll be applied to all action methods, and the code will be as tidy as it gets.

 

ASP.NET Core custom authorization attributes

 

That covers the general principle of how to use method attributes to define custom authorization for controller actions. But we might need to create permissions which are assigned per entity, as in the case of a Buyer trying to get a list of Products in a Warehouse. A Warehouse is identified by its WarehouseId, and that must be a parameter in the method for getting Products.

 

We’ll add a new constructor for defining the allowed WarehousePermissions in the ApiAuthorizationFilter:

 

ASP.NET Core custom authorization attributes

 

And we can add the ApiAuthorizationFilter attribute to the controller method for fetching the Products:

 

ASP.NET Core custom authorization attributes

 

But as WarehousePermissions are attached to specific Warehouses, we need a way to determine which method parameter of GetProducts represents the WarehouseId to be looked for. Attributes don’t have access to the parameters of the method through the constructor, and they’re created at compile-time, so they can only accept predefined objects or constants. On the other hand, the ApiAuthorizationFilter does have access to the ActionExecutingContext object, and it’s possible to look through the parameters inside OnActionExecuting.

 

So, one possible way to find the required method parameter is to add a string in the ApiAuthorizationFilter constructor and look for the parameter by its name.

 

ASP.NET Core custom authorization attributes

 

ASP.NET Core custom authorization attributes

 

Once we have that, we can then search for the given parameter and use it as a WarehouseId:

 

ASP.NET Core custom authorization attributes

 

ASP.NET Core custom authorization attributes

 

We can call this method in OnActionExecuting, and use the found WarehouseId to check (CheckPermissionsForValue) whether the UserPermissions object contains a list of WarehousePermissions for that Id. If the list contains any of the permissions, we specified in the attribute constructor, the controller method is allowed to return a result.

 

ASP.NET Core custom authorization attributes

 

The obvious downside to this approach is using ‘magic strings’ in the attributes, which means there is a typo just waiting to happen. Therefore, it would be nicer to have some more strongly typed manner of finding the WarehouseId in the method parameters.

 

Parameter attributes


In order to mark a controller action parameter as WarehouseId, we’ll create the custom parameter attribute WarehouseIdAttribute.

 

ASP.NET Core custom authorization attributes

 

It doesn’t need any internal logic. It’ll only be used to point out the needed parameter in the method signature.

 

ASP.NET Core custom authorization attributes

 

Once we’ve marked the correct parameter in the controller method, we can find it in ApiAuthorizationFilter. And the ‘magic strings’ are gone.

 

ASP.NET Core custom authorization attributes

 

Swagger integration

 

A useful thing for developing an API in ASP.NET Core is using Swagger to easily test the controller methods without actual client-side implementation. Regardless of whether the UserPermissions object is kept in the token or fetched on the back-end, knowing which permissions are needed for which API endpoint makes using the API much simpler. To make the life of the frontend developer a bit easier, we can add some simple adjustments to show the permissions we added using custom attributes.

 

In the ApiAuthorizationFilter class, a property named PermissionsString is added to create a user-friendly string from the permissions added to the object by the constructor.

 

ASP.NET Core custom authorization attributes

 

We’ll also create a class implementing the IOperationFilter interface, called PermissionsFilter. This IOperationFilter will be used by Swagger to produce the UI.

 

ASP.NET Core custom authorization attributes

 

The Apply method tries to find a filter in the ApiDescription, which has our custom type (ApiAuthorizationFilter). If one is found, the user-friendly permission string is added to the description for the operation. This PermissionsFilter is then added to the Swagger options, while services are configured.

 

ASP.NET Core custom authorization attributes

 

This simple adjustment makes the SwaggerUI show the required permissions for every API method.

 

ASP.NET Core custom authorization attributes

 

Wrap-up

 

Using custom attributes is a very practical and clean way to implement a custom authorization system for your ASP.NET Core API. It reduces the amount of redundant code and allows developers to make the permissions as granular as needed. By simple adjustments to Swagger (if it’s used), the front-end developers can also easily see which permissions an API method requires.

 

October 15, 2020

Worth sharing?

Your friends will appreciate it.

You might also be interested in other topics.