Azure Active Directory application model
- 1/22/2016
- The building blocks: Application and ServicePrincipal
- Consent and delegated permissions
- App user assignment, app permissions, and app roles
- Groups
- Summary
App user assignment, app permissions, and app roles
This section describes a set of Azure AD features that seem unrelated but are in fact all implemented through the same primitive: the role. Here’s the list of features I’ll cover.
- App user assignment The ability to explicitly define which users should be allowed to get a token for a certain application, at the exclusion of everybody else.
- App-level permissions The ability to expose (and request) permissions that can be assigned to applications themselves, as opposed to the users of the apps.
- App roles The ability for developers to define application-specific roles, which can be used by administrators to establish in which capacity users can access an application.
All these features give you even more control over who can access your application and how.
App user assignment
By default, every user in a tenant can request a token from any app. Whether the requested token will actually be issued depends on the outcome of user authentication, consent, and considerations of user versus admin permissions, as I’ve discussed in the preceding sections.
Azure AD offers the possibility for an administrator to restrict access to one application to a specific set of handpicked accounts. In terms of today’s experience, an administrator can navigate to the Azure management portal at https://manage.windowsazure.com, select the target tenant, navigate to the appropriate app entry, select the Configure tab, and flip the setting for “User assignment required to access app” to On.
Given that this feature is related to specific instances of the app in specific tenants, the knobs used to control it are not in the Application object but in the ServicePrincipal and associated entities in the target tenant. You already encountered the ServicePrincipal property appRoleAssignmentRequired: flipping the switch in the portal has the effect of setting this property to true.
The Users tab in the application entry in the Azure portal lists all the users that are assigned to the application. From now on, no user not on that list can successfully request a token for the application. If you flip the switch for one of the apps you’ve been playing with in the preceding section, you’ll see that all the users that already gave consent for the app are present in the list. Every time a user gives consent to the app, Azure AD adds an entry to a list of AppRoleAssignment, an entity I haven’t yet discussed. Here’s how one typical entry looks:
{ "odata.type": "Microsoft.DirectoryServices.AppRoleAssignment", "objectType": "AppRoleAssignment", "objectId": "Bkp-sDgT4kq5a-YB4HMf2q2NyOTf4hpKhVKXXQHxMhA", "deletionTimestamp": null, "creationTimestamp": "2015-09-06T08:53:30.1974755Z", "id": "00000000-0000-0000-0000-000000000000", "principalDisplayName": "Vittorio Bertocci", "principalId": "b07e4a06-1338-4ae2-b96b-e601e0731fda", "principalType": "User", "resourceDisplayName": "MarioApp1", "resourceId": "725a2d9a-6707-4127-8131-4f9106d771de" }
That entry declares that the user Vittorio Bertocci (identified by its objectId b07e4a06-1338-4ae2-b96b-e601e0731fda) can have access to the app MarioApp1 (object ID of the app’s ServicePrincipal being 725a2d9a-6707-4127-8131-4f9106d771de) in the capacity of role 00000000-0000-0000-0000-000000000000.
This is where the role of Role (pun intended) comes into play. As you will see later, Azure AD allows developers to define application-specific roles. The AppRoleAssignment entity is meant to track that a certain app role has been assigned to one user for a certain app. What you are discovering here is that Azure AD uses AppRoleAssignment also for tracking app user assignments—but in this case, Azure AD automatically sets in the AppRoleAssignment a default role, 00000000-0000-0000-0000-000000000000. It’s as simple as that.
One notable property of AppRoleAssignment is principalType. The sample entry here has the value User, indicating that the entity being assigned the role is a user account. Other possible values are Group (in which case, all the members of the group are assigned the role) or ServicePrincipal (in which case, the role is being assigned to another client application).
If you use the Azure portal to assign more users to the app, you’ll see corresponding new AppRoleAssignment entries appearing in the application. By the way, the query I used for getting the list of AppRoleAssignments for my app is:
https://graph.windows.net/developertenant.onmicrosoft.com/servicePrincipals/725a2d9a-6707-4127- 8131-4f9106d771de/appRoleAssignedTo.
Just for kicks, try to access your application with a user that has not been assigned. Instead of the usual consent dialog, you’ll get back a lovely error along the lines of:
- “error=access_denied&error_description=AADSTS50105: The signed in user ‘fabio@developertenant.onmicrosoft.com’ is not assigned to a role for the application ‘developertenant.onmicrosoft.com’.”
The behavior described in this section is what you would observe if your application didn’t define any app roles. In the next section, I’ll explore app roles in more depth.
App roles
Azure AD allows developers to define roles associated with an application. Traditionally, roles are handy ways of assigning collections of permissions to a bunch of users all at once: for example, people in a hypothetical Approver role might have read/write access to a certain set of resources, while people in the Reviewer role might have only read access to the same resources. Roles are handy because assigning a user to a role saves you the hassle of adding all the permissions a role entails one by one. Moreover, when a new resource is added to the milieu, access to that resource can be added to the role to enable access to it for all the users already assigned to the role, replacing the need to assign access individually, account by account. That said, roles in Azure AD do not necessarily need to represent permissions grouping: Azure AD does not offer you anything for representing such permissions anyway; it is your app’s job to interpret each role. You can use application roles to represent any user category that makes sense for your application, like what is the primary spoken language of a user. True, there are many other ways of tracking the same info, but one big advantage of app roles over any other method is that Azure AD will send them in the form of claims in the token, making it extra easy for the app to consume the info they carry.
After you declare application roles, such roles are available to be assigned to users by the administrators of the tenants using your app. Let’s take a look at how that cycle plays out.
The Application entity has one collection, appRoles, which is used for declaring the roles you want to associate with your application. As of today, the way in which you populate that property is by downloading the app manifest as described in “The Application” section at the beginning of this chapter, adding the appropriate entries in appRoles, and uploading it back via the portal. Here is what one appRoles collection looks like:
"appRoles": [ { "allowedMemberTypes": [ "User" ], "description": "Approvers can mark documents as approved", "displayName": "Approver", "id": "8F29F99B-5C77-4FBA-A310-4A5C0574E8FF", "isEnabled": "true", "value": "approver" }, { "allowedMemberTypes": [ "User" ], "description": "Reviewers can read documents", "displayName": "Reviewer", "id": "0866623F-2159-4F90-A575-2D1D4D3F7391", "isEnabled": "true", "value": "reviewer" } ],
The properties of each entry are mostly self-explanatory, but there are a couple of nontrivial points.
The displayName and description strings are used in any experience in which the role is presented, such as the one in which an administrator can assign roles to users.
The value property represents the value that the role claim will carry in tokens issued for users belonging to this role. This is the value that your application should be prepared to receive and interpret at access time.
The id is the actual identifier of the role entry. It must be unique within the context of this Application.
The allowedMemberTypes property is the interesting one. Roles can be assigned to users, groups, and applications. An allowedMemberTypes collection including the entry “User” indicates a role that can be assigned to both users and groups. (In the next section, I’ll cover roles assignable to applications.)
Once you have added the roles in the manifest file, don’t forget to upload it back via the portal.
If you head back to the Users tab and try to assign a new user to the app like you did in the preceding section, you’ll see that you are no longer able to simply declare that you want to assign a user to the app: now you are presented with a choice between the various roles you declared in the manifest. Assign one of the roles to a random user, and then launch the app and try to sign in with that user.
If you go back to the appRoleAssignedTo property of the ServicePrincipal and inspect the role assignments there, you’ll find the same user assignments from the preceding section, plus a new entry for the user you just assigned to a role. It should look something like this:
{ "odata.type": "Microsoft.DirectoryServices.AppRoleAssignment", "objectType": "AppRoleAssignment", "objectId": "9pcRosZaC0a10yoa5r0IwZrIr_JYzUxFtCmlWBYn6w0", "deletionTimestamp": null, "creationTimestamp": null, "id": "8f29f99b-5c77-4fba-a310-4a5c0574e8ff", "principalDisplayName": "Fabio Bianchi", "principalId": "a21197f6-5ac6-460b-b5d3-2a1ae6bd08c1", "principalType": "User", "resourceDisplayName": "MarioApp1", "resourceId": "725a2d9a-6707-4127-8131-4f9106d771de" },
As expected, the id property points to one of the roles just defined, as opposed to the default 00000000-0000-0000-0000-000000000000 used during user assignment.
Launch the app and sign in as the user you just assigned to the role. If you capture the traffic via Fiddler (as you learned about in Chapter 6) and peek at the JWT token sent in the id_token, you’ll notice a new roles claim:
{ "amr" : [ "pwd" ], "aud" : "1538b378-5829-46de-9294-6cfb4ad4bbaa", "c_hash" : "EOuY-5M5XFxRyNCvRHe8Kg", "exp" : 1442740348, "family_name" : "Bianchi", "given_name" : "Fabio", "iat" : 1442736448, "iss" : "https://sts.windows.net/6c3d51dd-f0e5-4959-b4ea-a80c4e36fe5e/", "name" : "Fabio Bianchi", "nbf" : 1442736448, "nonce" : "635783335451569467.YzJiYmZjMGUtOWFkMS00NzI3LWJkYjMtMzhiMjE0YjVmNWE0ZDcwZTk3YmY tNzQ4NC00YjkyLWFiY2YtYWViOWFhNjE0YjFj", "oid" : "a21197f6-5ac6-460b-b5d3-2a1ae6bd08c1", "roles" : [ "approver" ], "sub" : "OpcgG-Rxo_DSCJnuAf_7tdfXp7XaOzpW6pF3x7Ga8Y0", "tid" : "6c3d51dd-f0e5-4959-b4ea-a80c4e36fe5e", "unique_name" : "fabio@developertenant.onmicrosoft.com", "upn" : "fabio@developertenant.onmicrosoft.com", "ver" : "1.0" }
From your own application’s code, you can find out the same information through the usual ClaimsPrincipal.Current.FindFirst("roles") or, given that this is a multivalue claim, FindAll. Once you have the value, you can do whatever the semantic you assigned to the role suggests that your code should do: allow or deny access to the method being called, change environment settings to match the preferences of the caller, and so on.
If you are using roles for authorization, classic ASP.NET development practices would suggest using [Authorize], <Authorization>, or the evergreen IsInRole(). The good news is that they are all an option. The only thing you need to do is tell the identity pipeline that you want to use the claim type roles as the source for the role information used by those artifacts. That’s done via one property of TokenValidationParameters, RoleClaimType. For example, you can add the following to your OpenID Connect middleware initialization options:
TokenValidationParameters = new TokenValidationParameters { RoleClaimType = "roles", }
Azure AD roles are a very powerful tool, which is great for modeling relationships between users and the functionality that the app provides. Although the concept is not new, Azure AD roles operate in novel ways. For example, developers are fully responsible for their creation and maintenance, while the administrators of the various tenants where the app is provisioned are responsible for actually assigning people to them. Also, Azure AD roles are always declared as part of one app—it is not possible to create a role and reuse it across multiple applications. There is no counterpart for this on-premises. The closest match is groups, but those have global scope, and a developer has no control over them. Before the end of the chapter, I will also touch on groups in Azure AD.
Application permissions
All the features you encountered in this chapter are meant to give you control over how users have access to your app and how users can delegate your app to access other resources for them.
In some situations you want to be able to confer access rights to the application itself, regardless of what user account is using the app, or even when the app is running without any currently signed-in user. For example, imagine a long-running process that performs continuous integration—an app updating a dashboard with the health status of running tests against a solution and so on. Or more simply, think about all the situations in which an app must be able to perform operations that a low-privilege user would not normally be entitled to do—like provisioning users, assigning users to groups, reading full user profiles, and so on. Note that, once again, those kinds of permissions come into play when accessing the resource as a web API, so you won’t see this feature really play out until the next chapter. Here I’ll just discuss provisioning.
While delegated permissions are represented in Azure AD via oauth2Permission in the Application object and the oauth2PermissionsGrants collection in the ServicePrincipal table, Azure AD represents application permissions via Application.appRoles and ServicePrincipal.appRoleAssignedTo.
The AppRole entity is used to declare application permissions just as you have seen for the application roles case, with the difference that allowedMemberTypes must include an entry of value “Application”. To clarify that point, let’s once again turn to the Directory Graph API ServicePrincipal and examine its appRoles collection:
"appRoles": [ { "allowedMemberTypes": [ "Application" ], "description": "Allows the app to read and write all device properties without a signed-in user. Does not allow device creation, device deletion, or update of device alternative security identifiers.", "displayName": "Read and write devices", "id": "1138cb37-bd11-4084-a2b7-9f71582aeddb", "isEnabled": true, "value": "Device.ReadWrite.All" }, { "allowedMemberTypes": [ "Application" ], "description": "Allows the app to read and write data in your organization's directory, such as users and groups. Does not allow create, update, or delete of applications, service principals, or devices. Does not allow user or group deletion.", "displayName": "Read and write directory data", "id": "78c8a3c8-a07e-4b9e-af1b-b5ccab50a175", "isEnabled": true, "value": "Directory.Write" }, { "allowedMemberTypes": [ "Application" ], "description": "Allows the app to read data in your organization's directory, such as users, groups, and apps.", "displayName": "Read directory data", "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04", "isEnabled": true, "value": "Directory.Read" } ],
You can think of each of those roles as permissions that can be requested by applications invoking the Graph API. Although in the case of user and group roles, administrators can perform role assignments directly in the Azure management portal, granting application roles works very much like delegated permissions—via consent at the first token request.
A client application needs to declare in advance what application permissions (that is, application roles) it requires. That is currently done via the Azure portal, in the Permission To Other Application section of the Configure tab. In Figure 8-5 earlier, you can see that the middle column of the screen contains a drop-down labeled Application Permissions, in that case specifying the options available for the Directory Graph API. It is operated much as you learned about for the Delegated Permissions list, but the entries exposed in Application Permissions are the ones in the target resource from its appRoles collection, and specifically the ones marked as Application in allowedMemberTypes.
What happens when you select an application permission, say Read Directory Data, for the Directory Graph API? Something pretty similar to what you have seen in the case of delegated permissions. Take a look at what changes in the Application’s requiredResourceAccess collection:
"requiredResourceAccess": [ { "resourceAppId": "00000002-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04", "type": "Role" }, { "id": "78c8a3c8-a07e-4b9e-af1b-b5ccab50a175", "type": "Scope" }, { "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6", "type": "Scope" } ] }
The resource you want to access remains the same, the Directory Graph API—represented by the ID 00000002-0000-0000-c000-000000000000. In addition to the old delegated permissions, of type Scope, you’ll notice a new one, of type Role. The ID of this one corresponds exactly to the ID declared in the Directory Graph API’s ServicePrincipal appRoles for the Read Directory Data permission.
As I mentioned, granting application permissions takes place upon successful request of a token from the app and positive consent granted by the user at authentication time. The presence of an entry of type Role in a RequiredResourceAccessCollection introduces a key constraint, however: only admin consent requests will be considered. This means that every time you develop an app requesting application permissions, you have to be sure that the first time you request a token from it, you append the prompt=admin_consent flag to your request.
If you actually launch the app and go through the consent dance, you’ll find that after provisioning, the directory has added one new AppRoleAssignment entry to the appRoleAssignedTo property of the app’s ServicePrincipal entry in the target tenant. Or better, you would find it if your app had requested permissions for any resource other than the Directory Graph API. As I am writing this chapter, the Directory Graph API is the only resource that received special treatment from Azure AD: whereas every other resource has its consent settings recorded in the entities described in this chapter, as of today clients accessing the Graph API record the application permissions consent for it elsewhere. I won’t go into further details for two reasons. One, it would not help you understand how application permissions work in general, given that each and every other resource does use appRoleAssignedTo. Two, there is talk of changing the Directory Graph API behavior so that it will start acting like any other resource—it’s entirely possible that this will already be the case once the book is in your hands, but given that it’s not for sure, I am not taking any chances.
With their permission/role dual nature, application permissions can be confusing. However, they are an extremely powerful construct, and the possibilities their use opens up are well worth the effort of mastering them.