Lately, I’ve received a lot of questions related to roles-based access control (RBAC). It’s a great, clean way to define which users have which permissions, where a user is assigned to one or many “roles,” and your resources are protected by specifying which roles are allowed to access them. I recently wrote a post on basic RBAC for AAD using the Node.js backend, but today I’ll tackle a more generalized case, and I’ll do it using the new .NET backend preview.
Let’s assume I’m building a simple consumer forums application, in which I want an option to mark posts as “sticky.” This means it will always appear at the top of the posts list. My forum would likely end up in a bad state if I let all users do this, so I want to restrict the action to moderators only. All of my users will be authenticated via Facebook, and I’ll be keeping track of their roles (moderator or not) within a database table in my application.
So the question arises: how do I make it so that only moderators can mark a post as “sticky?”
To start things off, let’s take a look at the target programming model. I’ll be working with a ForumPost object and its associated controller. Since we’re making a change to an existing post we’ll be protecting the HTTP PATCH method. Here’s the controller, with just the PATCH method:
Looks nice and simple, right? We just decorate the method with an attribute specifying the allowed roles. It’s pretty similar to the AuthorizeLevel attribute that Mobile Services provides by default with the .NET backend. In fact, we’ll be replicating a bit of that attribute’s functionality as we go through this process.
Creating a custom authorization attribute
So first, let’s address the creation of an attribute. The basic premise is that you need to derive from the abstract class Attribute. In our case, we’ll be using the concrete subclass AuthorizationFilterAttribute, which will make things a bit simpler for us. There are basically two parts needed here: a constructor which turns the annotation into usable data, and an OnAuthorization() method which will get called when a user attempts to access the endpoint.
As you can see, I’m actually accepting an arbitrary number of roles, which would be separated by commas in the annotation. I’m then storing these in a HashSet for easy lookup when we get to OnAuthorization(). Everything is being handled as strings here, but you could certainly work with enums, which would make the experience a little cleaner when you go to protect your endpoints.
The other important thing to note is the AttributeUsage section, where I’ve said that the attribute can decorate both full controllers or just individual methods. Also, since I accept a variable number of arguments, I’ve gone ahead and restricted developers to using one instance of this attribute per method/controller.
Authorizing the user
Now we can focus on the OnAuthorization() method, which is where we will make the decision about whether or not the decorated method/controller can be accessed. We need to get a notion of the currently logged-in user and their roles, so first we need to make sure that someone is actually logged in. In doing so, we’re technically replicating the behavior of AuthorizeLevel attribute, as if we’ve set the value to “User.” After we’ve cleared that hurdle, we can check for roles.
One option is to, if applicable, use the associated identity provider’s graph. However, many cases will look similar to today’s scenario, where the role is completely application-level, perhaps stored in the database. You can see that I’m just querying my table to get the roles held by the user. Regardless of what method you choose, once you have your roles, you can just cross-check against the list specified by the attribute.
And that’s pretty much all you need to do! Just place this class in a location accessible to your backend project, and you have all you need to get started protecting your APIs at fine granularity. The approach should work across identity providers – in fact, you may have noticed that while I mentioned Facebook in the scenario description, nowhere in my code did I care where the user came from.
As a next step for the enterprise-inclined: you can take my post on RBAC with AAD (in Node) and combine those concepts with this solution pretty easily. I should hopefully have a post out soon covering more of the cool things you can do with AAD and the .NET backend, so keep an eye out for that.
Mobile Services Forums – In keeping with today’s theme, and because it’s a great place to turn if you have questions about anything Mobile Services.
Mobile Services UserVoice – While we’re at it, we’re always looking for feedback, so please make yourself heard here!
Getting Started with the .NET backend – There’s a lot of cool new things you can do with the new .NET backend. It should feel familiar to ASP.NET WebAPI developers, but even if you’re new to that stack, it’s easy to get started. Try it out!
Getting Started with Authentication in the .NET Backend – This shows you how to work with social providers. For AAD, see below.
Getting started with AAD in the .NET Backend – AAD for the .NET runtime is only available via a client flow right now. This tutorial will get you started.