One of the things that still requires you to modify the application manifest in Azure AD is when you want to define permissions/roles that your app offers.

Maybe one day we will see a UI for doing this, but until then it still requires a bit of work.

The aim of this article is to make that job easier through examples.

Delegated permissions for APIs

Let's say that we build an API backend for a Todo app.

We would like for a consuming app to be able to read the todo items of a user.

By using delegated permissions, the Client will make the call in the user's context. This means the access token will contain information about the user, as well as information about the calling app. The API can then easily filter the data so that only that user's data is returned.

Let's define that permission!

You can find the manifest by finding your app registration in Azure AD and clicking the Manifest button.

Here is how our permission could look like:

{
  "oauth2Permissions":[
    {
      "adminConsentDescription": "Allow access to read all users' todo items.",
      "adminConsentDisplayName": "Read access to todo items",
      "id": "43dc1069-125f-4aac-b554-7a837e049ed1",
      "isEnabled": true,
      "type": "User",
      "userConsentDescription": "Allow access to read your todo items.",
      "userConsentDisplayName": "Read access to your todo items",
      "value": "Todo.Read"
    }
  ]
}

There is a default permission with the value user_impersonation. You can't delete it straight away, but you can disable it by setting isEnabled to false.

A few things to note:

  • There are many other things in the manifest too, they were left out for clarity.
  • The id must be unique, so you need to generate one for each permission. I use [System.Guid]::NewGuid() in PowerShell quite a lot.
  • "type": "User" means this permission can be granted by a non-admin user.
    • Use "type": "Admin" if you want it to be grantable by admin only
  • The value is what will be sent in the token.
  • The admin consent description is different. If an admin consents to the app (with the prompt=admin_consent parameter), the created oauth2PermissionGrant will apply to all users in the directory. Individual consent will not be asked after that.

Let's see what a token would look like for a client app.

First we define an app that requires the delegated permission:

Selecting the API

Selecting the permission

Then we login to the app and consent to the permissions required:

Consenting to the permissions required

After that our app gets a token with the scope in it (some claims removed):

{
  "appid": "28d6c0d7-6017-42f7-8cee-c27d80bb9709",
  "family_name": "User",
  "given_name": "Admin",
  "name": "Admin User",
  "scp": "Todo.Read"
}

You can see the token contains the app's client id (appid), in addition to user info.

When the app calls the API and passes this token to it, the API knows what app made the call, as well as which user is signed in there.

Application permissions

Maybe you also want to allow an app to get the todo items of all users even when there is no one signed in.

Application permissions are essentially a role assigned to your app's service principal.

Once a role like this is assigned, the app can call the API whenever it wants, using its client id and secret (or certificate) as its credentials.

Here is how you would define the application permission for reading all todo items:

{
  "appRoles": [
  {
    "allowedMemberTypes": [
      "Application"
    ],
    "displayName": "Read all todo items",
    "id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
    "isEnabled": true,
    "description": "Allow the application to read all todo items as itself.",
    "value": "Todo.Read.All"
  }
  ]
}

Okay, a couple things:

  • allowedMemberTypes contains "Application" which makes this an app permission (we will get to other value it can have in the next section ("User"))
  • Once again, you need to generate a unique value for the id
  • The value is what will be sent in the access token

Let's specify that our app requires this permission as well:

Adding the app permission

Note a non-admin user cannot grant app permissions

This time the consent screen (which needed to be manually triggered using prompt=consent) looks a little different:

Consent app permission

Now if we get a token using the Client Credentials Grant Flow (some claims removed):

{
  "appid": "28d6c0d7-6017-42f7-8cee-c27d80bb9709",
  "roles": [
    "Todo.Read.All"
  ]
}

This time there is no scp claim. There is only a roles claim that contains the value for our app permission.

Roles for users

Remember how we defined that app permission with "allowedMemberTypes":["Application"]?

Well, what if we wanted to use role-based access in our client app?

For example, it would be nice to have an admin role.

We can define that an app has roles which can be assigned to users in a very similar fashion to app permissions.

{
  "appRoles": [
    {
      "allowedMemberTypes": [
        "User"
      ],
      "displayName": "Administrator",
      "id": "179c1dc0-4801-46f3-bc0d-35f059da1415",
      "isEnabled": true,
      "description": "Administrators can access advanced features.",
      "value": "admin"
    }
  ]
}

Important things:

  • allowedMemberTypes now contains User, meaning this role can be given to a user
    • Or a group if you have AAD Basic/Premium
  • The id must once again be generated
  • The value will be in the roles claim

So let's assign the role to a user.

First we need to find the service principal. One way is to click the Managed application in local directory link:

Managed application link

Service principal = Enterprise app = Managed application in local directory. Great naming is great.

The reason we have to go the service principal's blade is because you can't assign users on the app itself.

Roles are always assigned on the service principal. Delegated and app permissions too.

Alright, so let's add a user:

Add user

Find the user we want:

Find user

In this case we can't select a role since our app only has one :)

After assignment:

After assigning role to user

Now when the Test User logs in we get an id token with claims like this:

{
  "family_name": "User",
  "given_name": "Test",
  "name": "Test User",
  "roles": [
    "admin"
  ]
}

Other users can of course still sign in to the app, unless you enable the User assignment required switch in the service principal's Properties. Then only users who have been assigned a role will have access to the app.

Note that you can also assign roles to groups if you have AAD Basic or Premium. That allows a user to have multiple roles. With user-specific assignment a user can only have one role.

Programmatic assignment

To do programmatic assignment, I urge you to play around with the Azure AD Graph API.

Delegated permissions can be granted for a service principal by creating the right oauth2PermissionGrant on it.

App permissions can be granted by creating an appRoleAssignment on the service principal. Same goes for user roles.

Conclusions

Azure Active Directory allows you quite a lot of control for defining application and user access.

Hopefully this article makes it easier for you.

Here are some links that you may find helpful as well:

As usual, feel free to leave comments :)