Managed Identity auth with simple app authentication (via Add identity provider from the Authentication tab of the app that needs to be protected) is outlined here. This covers the most rudimentary case.
However, often we want to be more specific as to which actions should be protected and which not, where something such as what is outlined here would be done. In this case code is used to configure Auth via the Services.AddAuthentication pattern or the like.
Calling such a web API with Managed identity is achieved using app roles described here
App roles are necessary for app to app communication as apps cannot consent to granular permissions.
The following steps outline how to enable app to app communication via Managed Identity using app roles taken from here:
- Enable managed identity on the resource
- Copy the Object ID from the identity tab of the resource -> $miOid
- Copy the Object ID of the Enterprise Application which represents the app the resource need to authenticate with -> $serviceOid
- Create an app role for the App Registration of the app the resource needs to communicate with and copy the appRoleId eg. 'access_as_application' (the name is up to you)
- Run the following script
$miOid="<step_2>"
$serviceOid="<step_3>"
$appRoleId="<step_4>"
$body = (@{
principalId=$miOid
resourceId=$serviceOid
appRoleId=$appRoleId
} | ConvertTo-Json -Compress).Replace('"', '\"')
az rest -m POST -u https://graph.microsoft.com/v1.0/servicePrincipals/$miOid/appRoleAssignments -b $body
If successful the result should appear as follows
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('<miOID>')/appRoleAssignments/$entity",
"appRoleId": "<appRoleId>",
"createdDateTime": "2022-10-31T11:57:11.6563782Z",
"deletedDateTime": null,
"id": "vkSTufiNCkmE1dDhZDxE1dFLII64-WVOpNL5Trgm-3w",
"principalDisplayName": "<miAppName>",
"principalId": "<miOid>",
"principalType": "ServicePrincipal",
"resourceDisplayName": "<serviceAppName>",
"resourceId": "<serviceOid>"
}
Code for calling the API is as follows
NOTE: you should likely be using HttpClientFactory for this.
var httpClient = new HttpClient();
var azureCredential = new DefaultAzureCredential();
var context = new TokenRequestContext(
new string[] { "api://<app_id>/.default" }); // the Application ID URI copied from the protected app App Registration + /.default
var token = await azureCredential.GetTokenAsync(context);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
var result = await httpClient.GetStringAsync("https://service_app.azurewebsites.net/");
The calling API will receive a token with the appropriate role claim defined above
The receiving API can than verify that role as follows:
[Authorize(Roles = "access_as_application")]
With more details outlined in the original link describing protecting a web api from the start of this gist