Trying to access MS Graph as web app running on App Service, according to instructions found here:
Tutorial: Access Microsoft Graph from a secured app as the app
Everything works fine when run locally. The same code however returns a faulty access token when run in Azure.
This code to set up my GraphServiceClient
runs fine locally:
var credential = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new EnvironmentCredential());
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://graph.microsoft.com/.default" })
var accessToken = token.Token;
var graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider((request) => {
{
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
return Task.CompletedTask;
}));
When run in Azure the accessToken
retrieved this way is incorrect. These are the differences:
appid
: the correct appid
of my deployment slot locally; incorrect in Azure.appidacr
: "1" locally; "2" in Azure.app_displayname
: display name of my deployment slot locally (MyApp - Dev
); deployment slot name in Azure (myapp/slots/dev
).roles
: correct locally ("AppRoleAssignment.ReadWrite.All"
); missing in Azure.Obviously, the next call with this GraphServiceClient
returns Status Code: Forbidden
since there are no roles in the token.
Microsoft.Graph.ServiceException: Code: Authorization_RequestDenied
I already tried:
Microsoft.Web
and Azure.Identity
to the latest version (2.5.0 and 1.8.2 resp.)EnvironmentCredential
. (AZURE_CLIENT_ID
, AZURE_CLIENT_SECRET
, and AZURE_TENANT_ID
)new ChainedTokenCredential(...)
with new DefaultAzureCredential()
"https://graph.microsoft.com/.default"
) with a specific one ("https://graph.microsoft.com/AppRoleAssignment.ReadWrite.All"
).All with the same result.
I have no clue, can only guess Azure.Identity
does not work well with deployment slots? Which would be strange since they all have their own service principal.
As it appears, when assigning API Permissions to a web app, the Application ID is used and not the managed principal (?)
That explains why this code works locally but not on Azure.
var credential = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new EnvironmentCredential());
Locally there is no managed identity, so the chained token credential will only take credentials stored in environment variables into account. Credentials are stored in environment variables for developing locally
On Azure with a managed identity on the web app, the chained token credential will use that first, and fail when requesting an access token since the API permission was set on the Application ID, not the managed identity.
The solution in my case was to store credentials (Client ID, Client Secret) in environment variables both locally and on Azure, and only use environment variables to request the access token.
var credential = new EnvironmentCredential();