We have implemented a zero-configuration, easy to use Authorization and permission system. There are multiple ways to ensure that a user does not have access to pages or Api methods they shouldn't have access to. All of them are implemented using the [Authorize] attribute. There are three flavors of this attribute.

  • [Authorize] This is the simplest ones and it simply checks that the current user is logged in.
  • [Authorize(Roles="admin")] When applied, this checks that the current logged in user has the role of an admin. Specify multiple roles by separating them via commas.
  • [Authorize(Internal.Permissions.Administration.View)] This is how we implemented permissions (also known as policies). When applied, this checks that the current logged in user has the given permission.

If you want to apply an authorization rule on a Blazor page, use @attribute [Authorize(Internal.Permissions.Administration.View)], or any other flavor of the Authorize attribute. Similarly, if you want to apply an authorization rule on a controller method (or on the entire controller), use [Authorize(Internal.Permissions.Administration.View)]. See the existing code for examples.

Roles and Permission Configuration

As mentioned above, the system we have implemented is zero-configuration. All roles are defined in ServiceConfiguration.Identity.Roles and will be automatically created in IdentityDataInitializer.SeedRoles if they do not exist.

Similarly, permissions are auto-generated, but the mechanism involved is a bit more complex. First, all permissions need to be defined in Internal\Permissions\Permissions.cs as constants. You then decorate each constant by using the PermissionRoles attribute and specifying one or more comma separated roles as the attribute argument. You will be referencing these constants whenever you use the [Authorize] attribute with a policy specification. These constants are auto-discovered by means of reflection, so make sure they are inside public classes in the AppName.Internal.Permissions namespace. The constants must also be public. You can set the PermissionRoles attribute on a given class (optional), in which case it will take all the public constants in that class and create the appropriate permissions. You an also override an individual permission constant.

The magic happens inside the PermissionAuthorizationHandler and PermissionPolicyProvider classes by means of the concept of Claims. We won't go into great detail here, but if you are interested, check Microsoft's documentation. When creating a new permission, you can look at the AspNetRoleClaims table to ensure that it is auto-created. If it is not there, your permission will not work when applied to the [Authorize] attribute.

Managing User Roles

By default all registered users are given the User role, as defined in ServiceConfiguration.IdentityConfiguration.UserRoleName. You can modify this behavior in Register.razor, specifically the SubmitClicked method. To modify the roles of one or more users, head over to the Users page (must be logged in as an Admin). You can assign one or more roles to one or more users. These users will then get the permissions that were configured via the PermissionRoles attribute.

Table of Contents

An error has occurred. This application may no longer respond until reloaded. Reload 🗙