The claims contained in the token returned by Azure AD depends on the OAuth2 grant type being used. When using a Client Credentials flow it implies that two applications, of which neither involves any user interaction, are being used. Azure documentation uses the terms daemon app and web API app. The daemon app is the application calling API's of the web API app.
The obvious but unfortunately wrong way is to use "Expose an API" e.g. on Azure portal: AD -> application -> Expose an API to create permissions. The problem is that "Expose an API" creates only delegated permissions. But delegated permissions are only relevant when a user is involved. Which is not the case when using a Client Credentials grant.
What one needs to create are application permissions. These permissions can, so it seems, currently only be created directly in the Manifest. The following is needed to create an application permission:
- Go to Web API app in AD -> click on Manifest
- Change the appRoles field to something like this:
{
// ...
"appRoles": [
{
"allowedMemberTypes": ["Application"], // Must be "Application"
"description": "Allows Read operation",
"displayName": "Read",
"id": "a35fcf6e-58c4-42af-937d-f43e90103b44", // A unique UUID
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "Read" // The role one wants to create
}
]
// ...
}
- Save the file
More information can also be found on the official Azure documentation page Protected web API: App registration.
Now one can go to the daemon app in the AD and grant the created application permissions. In Azure portal this is done by these steps:
- Go to Daemon app in AD
- Go to API permissions
- Click on Add a permission
- Select My APIs
- Select the Web API app
- Select Application permissions
- Now select the permissions the Daemon app gets granted
- Click on Add permission
- Grant admin consent by clicking on Grant admin consent for ...
The daemon app can now request a token using client credentials grant. The scope in the request must be '/.default'. (Only for delegated permissions one can ask for non-default scope.)
The returned token will then contain the claim roles which is a list of granted permissions. The permissions in the list are the permissions granted to the Damon app. E.g.
"roles": ["Read"]