Granting custom roles to an Azure AD application (without Tenant-Admin Consent)

In order to implement RBAC on top of our issued OAuth JWT access tokens, we’d like to grant Service B a custom role of Service A.

Usually, the above is implemented via OAuth scopes, and you might’ve seen that in Azure AD’s ‘Expose an API’ blade

However, adding custom scopes, at least in Azure AD — only applies for OAuth flows with user consent (not client credentials).

When using the client credentials flow, we must fallback to use application roles instead — if we were to try to add our configured Service.A.Reader role via the API permissions blade in Azure Portal, we’ll be quick to note that this operation requires tenant-admin consent;

In large enterprises, though, a tenant-admin consent might involve quite a bit of ceremony — dealing with in house ticketing systems, or talking to a remote IT / Ops department might take up to a few days, in some cases.

To grant a custom role to an application, without the need of admin consent, we can instead use the Microsoft Graph API (Note: There’s no way to execute this operation within Azure Portal, as of the time of writing).

Using Graph API to grant roles to a registered application

We’ll be invoking the appRoleAssignment creation API from Microsoft Graph on service A — this could be easily invoked via Azure CLI:

$> az rest \

--method post \

--uri https://graph.microsoft.com/beta/servicePrincipals/<service-a-enterprise-object-id>/appRoleAssignments \

-- headers "{\"content-type\": \"application/json\"}" \

-- body "{\"appRoleId\": \"<roleId>\", \"principalId\": \"<service-b-enterprise-object-id>\",

\"principalType\": \"ServicePrincipal\", \"resourceId\": \"<service-a-enterprise-object-id>\"}"

Let’s break it down:

We are invoking the appRoleassignments creation Graph API on the Enterprise Application (more on the difference between an Enterprise Application and Application registration below) of Service A (Denoted by the Object ID) — note that this is done similarly to how you would control access to Azure resources, like Azure Key Vault, where you add client grants to the ‘server’ service

We are passing it a role ID (In this tutorial, the Service.A.Reader role has an ID of 13371337–1337–1337–1337–133713371337)

We are passing in the principal ID of the Enterprise Application of Service B (Denoted by the Object ID)

The principal type is “Service Principal”

We pass in a target resource, which is once again, the Object ID of the Enterprise Application of Service A

In order to get the proper Object ID of both services, you should:

Go to the App registration blade in Azure AD (as shown in previous steps) Go to the relevant application (as shown in previous steps) In the Overview tab, verify the application has an Enterprise Associated with it, by looking at the “Managed application in local directory” div

4. If you see a ‘Create Service Principal’ link like above, click on it and wait for a few minutes — this will create an Enterprise Application instance for your app registration

5. Once an Enterprise Application is created, you should see have a hyperlink pointing to it in the same div above

6. Clicking on the above will lead you to the relevant Enterprise Application, which will show the Object ID you should use in the REST call above

7. Alternatively, you could search your application in the Enterprise Applications blade of your Azure AD instance

A successful invocation should look similar to the below:



{

"

"appRoleId": "13371337–1337–1337–1337–133713371339",

"creationTimestamp": "2019–10–21T18:20:53.9088367Z",

"id": "Hl8A-br-1kugbGxg1gpt2sQktBGJedJNiheeF4x1KBo",

"principalDisplayName": "Service B",

"principalId": "f9005f1e-feba-4bd6-a06c-6c60d60a6dda",

"principalType": "ServicePrincipal",

"resourceDisplayName": "Service A",

"resourceId": "38b8c0f9–837a-4abd-816f-bc51282519e2"

} This command is in preview. It may be changed/removed in a future release. @odata .context": " https://graph.microsoft.com/beta/$metadata#appRoleAssignments/$entity ","appRoleId": "13371337–1337–1337–1337–133713371339","creationTimestamp": "2019–10–21T18:20:53.9088367Z","id": "Hl8A-br-1kugbGxg1gpt2sQktBGJedJNiheeF4x1KBo","principalDisplayName": "Service B","principalId": "f9005f1e-feba-4bd6-a06c-6c60d60a6dda","principalType": "ServicePrincipal","resourceDisplayName": "Service A","resourceId": "38b8c0f9–837a-4abd-816f-bc51282519e2"

We may now verify that the role was in fact granted, by:

Going to the Enterprise Applications blade within Azure AD

2. Clicking on the Enterprise application instance of Service A (the ‘Server’ app)

3. Going to the Users and Groups blade

4. The users table should show Service B, in a fashion similar to the below: