Roles-Based Access Control in Mobile Services and Azure Active Directory

In November, we announced a preview of Azure Active Directory (AAD) as an identity provider for Mobile Services. The mission was to give enterprise developers an easy solution for building employee-facing mobile apps. Basic authentication is a great start, but in many cases our current preview customers have needed to distinguish between types of users in order to make authorization decisions. Roles-based access control (RBAC) is the idea of assigning permissions to roles that your users can hold, nicely defining boundaries on what certain classes of users can and cannot do. Luckily, it is very easy to add basic RBAC to Azure Mobile Services, and this post will walk you through how to accomplish that.
In order to follow along with the rest of this post, you’ll need to be enrolled in the Mobile Services preview for Azure Active Directory integration. If you would like access, please email MobileServices@microsoft.com.

 Background

Let’s imagine that I want to develop an application just for my sales team. In order to access the app, a user not only has to be a member of my company’s directory, but also assigned to the sales group.  Let’s take a look at the logic you’d need to add on the server-side to ensure that only authenticated members of the sales team can access the app. (If you’re just getting started with Azure Active Directory Authentication in Mobile Services, you may find this getting started tutorialhelpful.)

The basic approach we’ll be taking is to leverage security group membership for users in our Azure AD tenant. AAD has the concepts of both roles and groups, but in today’s scenario we’re working with an existing group that already has the correct user membership. We’ll be managing our group from an on-premises AD tenant that is synced with our Azure AD tenant. Customers of O365 and Windows Intune can attest that you get a lot of power by setting up a directory sync with on-premises Active Directory (and you can even use those tenants to build Mobile Services).

 

Today, I’m going to be using the “Password Sync” option, but there are multiple scenarios supported. You can actually set up AAD to point to ADFS for a great hybrid scenario. If you want to play with these options, spin up an Azure VM running Windows Server 2012 R2 Datacenter and install the Active Directory Domain Services role. Then follow the directory sync instructions.

Creating Groups

Within my directory, you can see that I’ve created some users (Alice, Bob, Carol, and Dave) as well as a “Sales” domain security group. I’ve made Alice and Bob members of this group, but Carol and Dave remain outside it and should not have access to my app. Everything else is default.

Connecting to Azure Mobile Services

So I’m now ready to start building my app backend. In an Azure mobile service, we want to protect each of our scripts and APIs with additional authorization logic on top of what is provided. For some extra sanity, I’m going ahead and setting permissions to “Only Authenticated Users” for each protected endpoint.

Because I’m building logic that I want executed in multiple scripts, I’m putting my code in the shared scripts section of my mobile service’s Git repository. I’m naming the script rbac.js.

The first step to determining group membership is to gain access to the AAD graph API. Setting this up is covered in this blog post as well as in this sample. The following will get you started:

Once we have the graph access token, we need to call the isMemberOf graph endpoint. This will check if the specified user is a member of a given group, including transitive membership. We can get the user ID from the script being checked – all table scripts explicitly receive a user object, and you can get one from a Custom API by accessing request.user. We’ll need to obtain a group ID, which is conveniently available in the management portal. Head into your Azure AD tenant, open the groups tab, select the group, and copy the Object ID from under the configure tab.

For ease of use, we’ll export the value from our shared script with a friendly name.

We’ll now want to write a function to wrap our call to the AAD isMemberOf endpoint. As mentioned before, we’ll need both the userID and the groupID we just obtained. The request also needs to include the access token that we obtained.

We’ll simplify the programming model a bit and just require the Mobile Services user object (from which we can get the objectID), as well as the group ID. We’ll wrap it up nicely to get the graph token for us and make the call. Note that in production, you’ll really want to cache this token instead of fetching it every time. The token contains an expiration value that you can use to determine when you should fetch a new one.

That’s all for the shared script. Now, for each script that I want to protect with RBAC, I just have to add a few lines and perform the script’s work in my callback. Here’s an example for a table read:

Wrapping Up

And that’s it. We’ve successfully restricted our app usage to the subset of our employees that needs it. From here, you can start building for a variety of RBAC scenarios. If you find that you want to differentiate the client UI for certain users, one simple way to do this is to expose the membership check as a Custom API, which you can invoke immediately after login.

Azure Active Directory is doing some cool things with their group support, and a lot can be accomplished straight from the management portal. AAD Premium users should be sure to check out the new self-service group management support.

If you are interested in enterprise mobile app development, I encourage you to also check out the new Mobile Services .NET backend preview. It does not currently have built-in AAD support, but don’t worry! We will have it available soon.

And again, if you’re interested in joining the AAD preview or have any questions, please reach out to us at mobileservices@microsoft.com.

If there are additional features or scenarios you’d like to see covered, please submit your feedback to our uservoice.