Using ASP Security Kit: Getting started with declarative permission based authorization in ASP.NET Mvc

Activity based authorization can also be referred as permission based or action based authorization. The idea is that every action (activity) that an application exposes must have a corresponding permission code unique for that action. The uniqueness is desirable but not always practical; therefore ASP Security Kit does give you option to re-use permission codes (which is useful in certain scenarios).

Read this paper to get a conceptual overview of what is permission based authorization, why you should always prefer it over role based approach that ASP.NET traditionally offers and how permission based authorization is implemented in ASP Security Kit along with instance (resource) awareness.

UserService and Data Models

As stated here, UserService is the primary service in the core library that manages, among other things, registration, login, password reset, verification, user session, permission set and granting/revocation of permissions. These APIs are consumed not only by your application but also by ActAuthorize to give you declarative, Mvc filter-based model to implement activity-based, instance-aware authorization in your web application.

UserService depends on IUserDAL to save and retrieve user data from your preferred data store using your preferred data access technology.

The core library defines interfaces for both user and permit models and the implementation of IUserDAL is responsible to accept and return concrete instances of these models.

Enable permission based authorization for all of your controllers and actions

Just by defining ActAuthorize attribute on the base controller as given below, you enable permission based authorization for all the actions inside that controller and of any controller that derives from it.

[ActAuthorize(ServicesKey = “ContextServices”, Verified = true)]
public class SiteControllerBase : Controller, IUnauthorizeAction
{
}

Context Services

IContextServices aggregates services required by ASP Security Kit: User, locale, session, logger ETC. ActAuthorize needs instance of IContextServices to perform its tasks. It looks for that instance in the HttpContext.Items collection with the key specified by ServicesKey property. This is set in global.asax.cs on Application_PostAcquireRequestState for each request.

3 security checks and their sequence

ActAuthorize is capable of performing three checks for a given action, and it does so in the following sequence:

  1. Whether or not User is authenticated.
  2. Whether or not User is verified (ignored if Verified property is not set to true.)
  3. Whether or not User is authorized (activity based and instance aware authorization check).

Verification is a premium feature of ASP Security Kit and is useful for most real world applications that require user to verify email/phone number that they input during registration, before user can do anything important with the application.

A check is only executed if its preceding check is satisfied positively. This is important to understand if you explicitly indicate to skip a check for a particular action/controller.

AllowAnonymous Attribute

If you want to opt out a particular action so that even anonymous users can execute it, decorate that action with Mvc’s AllowAnonymous attribute. As indicated above, this will skip verification and permission based checks as well since authentication is the first check and is not satisfied positively.

AllowNotVerified Attribute

If you have set ‘Verified’ to true and you want a particular action to opt out of verification requirement, decorate that action with AllowNotVerified attribute.

As indicated above, this will skip permission based check as well since verification precedes authorization and is not satisfied positively.

This attribute is useful to allow unverified user access to his profile so that he can change things like his email/username but cannot use other features of the application.
You can also define AllowNotVerified on a controller in which case, all the actions of that controller skip both verification and permission based authorization. This is not recommended as there’s no way to turn either of them on for a particular action inside that controller. Explicit definition on an action makes it more verbose to you and other devs when the code is visited later and therefore is recommended.

SkipActionAuthorization Attribute

If you want to have a particular action opt out of permission based authorization check, decorate it with SkipActionAuthorization attribute.

You can define SkipActionAuthorization on a controller in which case, all the actions of that controller skip permission based authorization. This is not recommended as there’s no way to turn it on for a particular action inside that controller. Explicit definition on an action makes it more verbose to you and other devs when the code is visited later and therefore is recommended.

Inference of permission code

ActAuthorize can automatically build up a permission code for currently executing action and this is the default behavior. For doing so, it follows the convention proposed in this paper. In short, a unique permission code for an action can be constructed by joining the entity name with action name. Often we create separate controller for each entity in our domain models and name that controller accordingly. For this reason, ActAuthorize by default uses values of action and controller tokens in the route data dictionary to construct the permission code:

public Class PostController : SiteControllerBase
{
    Public ActionResult Create()
    {
    }
}

In the example above, the permission code ActAuthorize generates will be ‘CreatePost’ when validating authorization for create action. You can override this default behavior for either or both of the segments of permission code by using attributes.

AuthEntity Attribute

By default, controller name is used as the value for entity type segment to construct unique permission code for an action. You can override this default behavior by defining AuthEntity attribute on a controller providing with the desired value for the only required argument – permissionCode – it defines.

[AuthEntity(“User”)]
public Class PermissionController : SiteControllerBase
{
    Public ActionResult Grant()
    {
    }
}
    

In the example above we used AuthEntity to specify that ‘User’ should be used to construct permission code. Now for actions such as Grant in this controller, the permission code ActAuthorize generates will be GrantUser.

AuthAction Attribute

By default, action name is used as the value for action segment to construct unique permission code for an action. You can override this default behavior by defining AuthAction attribute on an action method providing with the desired value for the only required argument – permissionCode – it defines.

[AuthEntity(“User”)]
public Class PermissionController : SiteControllerBase
{
    [AuthAction(“Edit”)]
    Public ActionResult Grant()
    {
    }
}
    

In the example above we used both AuthEntity and AuthAction to explicitly specify entity and action that together make up a permission code. Now when the Grant action is executed, the permission code ActAuthorize generates will be EditUser.

Specify complete permission code at once

Both AuthEntity and AuthAction attributes consist of an additional property called ‘IsComplete’. As its name implies, this instructs ActAuthorize whether or not to consider permission code specified in this attribute as partial or complete. The default value is false which implies the code specified is only one of the two segments required to construct the permission code.

Now in some rare cases you may want to use a particular permission code for all the actions in a controller as in,

[AuthEntity(“EditUser”, IsComplete = true)]
public Class PermissionController : SiteControllerBase
{
    Public ActionResult Grant()
    {
    }

    Public ActionResult Revoke()
    {
    }

    [AuthAction(“IndexUser”, IsComplete = true)] // Contrived example.
    Public ActionResult Index()
    {
    }
}

In the example above we defined AuthEntity attribute on the controller with permission code as ‘EditUser’ and set IsComplete to true. Now for both Grant and Revoke action, the permission code ActAuthorize uses will be ‘EditUser’.
However, we defined AuthAction attribute on Index action with IsComplete = true. Therefore for this particular action, ActAuthorize will use permission code ‘IndexUser’ because AuthAction overrides AuthEntity if IsComplete = true.

Note: if IsComplete = true for AuthEntity, and you define AuthAction for one or more actions in that controller, you must set IsComplete = true for AuthAction as in above, otherwise both code will still be merged and you’ll have an unexpected permission code generated (like ‘IndexUserEditUser’) and authorization check will fail.

Instance (Resource) Aware permission checks

As explained in this paper, permission based authorization is not complete in and of itself until it can authorize users against entity instances (records) independently. Most actions are performed on some existing data. In a multi user environment data must be actively protected from being accessed and tampered by users whom that data doesn’t belong to.

Before proceeding further, it is important to talk a bit about types of permissions and actions. There are two kinds of actions – actions that generate new data and actions that operate on existing data. For example, create generates new data while view/edit/delete operate on existing data.

Based on this distinction, ASP Security Kit defines two types of permission codes – general (for actions that generate data) and instance (for actions that operate on existing data).
There’s in fact a third type, called duel which is nothing but extending an instance permission to be assumed as general, so as to be able to disregard instance aware check for specific entity types or for specific users (like admin).

In order to authorize user actions against some data, there must be a way to uniquely identify that data. Generally this is done by creating a key column which holds a unique value across all the records of that entity type.
Similar to how ActAuthorize extracts and constructs permission code, the same way it extracts key value for entity record to be operated upon by the executing action.

The default behavior is to pick value from request parameters using the parameter name ‘id’. You can override this behavior for all at once while defining ActAuthorize:

[ActAuthorize(ServicesKey = “ContextServices”,
    IDParameterName = “entityId”
    )]
public class SiteControllerBase : Controller, IUnauthorizeAction
{
}
    

IDParameter Attribute

You can also further instruct ActAuthorize to use a different key parameter name for all actions in a controller or for a specific action by defining IDParameter attribute on the controller or on an action, respectively.

[AuthEntity(“EditUser”, IsComplete = true)]
public class PermissionController : SiteControllerBase
{
    [IDParameter("userId")]
    public ActionResult Grant(UserPermit model)
    {
    }
    [IDParameter("userId")]
    public ActionResult Revoke(int id, int userId)
    {
    }
}

In the above example, we have indicated that we want to use "EditUser" permission to authorize actions in this controller. Since this permission code belongs to 'User' entity we need parameter 'userId' to authorize grant/revoke actions and not parameter 'id' because 'id' holds value for the key column of entity 'Permit '.
Of course, you can change above code and define IDParameter attribute on the controller and have it automatically applied for all the actions. Also, you can even override IDParameter defined on the controller by re-defining it on a particular action if you need so.