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 settingisEnabled
tofalse
.
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
- Use
- 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 createdoauth2PermissionGrant
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:
Then we login to the app and consent 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:
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:
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 containsUser
, 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 theroles
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:
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:
Find the user we want:
In this case we can't select a role since our app only has one :)
After assignment:
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:
- Understanding the Azure AD application manifest (Official docs)
- Integrating applications with Azure Active Directory (Official docs)
- Roles based access control in cloud applications using Azure AD (by Dushyant)
As usual, feel free to leave comments :)