Reputation: 1181
I'm fairly confident I set everything up right for a multi-tenant app.
In Tenant A: I created an Azure function and enabled a system managed identity. I granted the Managed Identity permissions to the Graph API. I confirmed my API can obtain an access token for the Managed Identity and access the Graph API with it. I added Azure AD authentication to the app and created an App Registration for the app. I configured the necessary Graph API permissions in the API Permissions settings of the app registration. I also enabled the various options to enable multi-tenant access.
In Tenant B: I accessed the special URL to start the admin consent process for the app in Tenant A. I confirmed that I was prompted to consent to the permissions that I specified in the app registration in Tenant A. I can see that a couple new enterprise application entries were created in Tenant B for the app in Tenant A.
So far so good. However, no matter what I do, I cannot obtain a token to access the graph API in the context of Tenant B. It ALWAYS gives me a token for the managed identity in Tenant A, and accesses the info in Tenant A.
The code the retrieves an access token using the managed identity is here:
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { AdditionallyAllowedTenants = { "*" }, TenantId = "<Tenant B ID>" });
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://graph.microsoft.com/.default" }, null, null, "<Tenant B ID"));
var accessToken = token.Token;
Now, I have tried all sorts of different combinations here. But, from everything I read, I am supposed to be able to specify the Tenant ID of another tenant in these methods and obtain an access token for that specific tenant. However, even when I specify Tenant B's ID, I always get a token back for the managed identity in Tenant A. If I use the token, of course, I end up accessing all of Tenant A's information.
Ironically, if I remove the DefaultAzureCredentialOptions
and include the tenantID in the GetToken
request I get an error telling me that I'm trying to obtain a token for a different tenant (than my own) and that I need to add the AdditionallyAllowedTenants
option. It clears the error up when I add that, but then it doesn't obtain a token for the other tenant.
Perhaps I am still approaching this wrong. I want to host a multi-tenant Azure Function that runs in the context of the Tenant where the request originated from to access information within that tenant using a managed identity that exists within that tenant. I can obtain the Tenant ID context from the claims sent to the Azure function during authentication, but no matter how I try to specify that Tenant ID in my code, it will not get a token for that Tenant.
UPDATE and SOLUTION: Thanks to Philippe's great write up, I did finally get this. I had some confusion around the mechanisms at play here and want to add this note for clarity on how I solved it.
If you've followed most of the documents you have an Azure Function app in your "host" tenant A, with an associated app registration in tenant A. The app registration has certain API permissions configured, and you have consented to this app and permissions in Tenant B.
To access the resources in Tenant B, you need to create a secret key or certificate on the app registration in Tenant A. You then need to authenticate using the Client ID, and secret key/certificate of the app registration in Tenant A, but you will request a token for Tenant B. Here it is in code using a certificate for authentication:
var appToken = new ClientCertificateCredential(tenantID, appID, appCert, new ClientCertificateCredentialOptions { AdditionallyAllowedTenants = { "*" } });
var graphServiceClient = new GraphServiceClient(appToken);
Here, we assign the following to the variables:
We have to include the AdditionallyAllowedTenants config parameter, and the *
authorizes a token from any tenant. We then take that credential and use it to build a new GraphServiceClient
that is connected to Tenant B.
Upvotes: 2
Views: 6262
Reputation: 14336
You've created two separate and independent identities for your service:
The DefaultAzureCredential will attempt different ways of authenticating until it finds one that works:
For your scenario, you basically have two options today.
Place the credentials for your multi-tenant app registration in environment variables and your existing code will "just work".
For example:
AZURE_TENANT_ID
- the target tenant where you want to obtain the tokenAZURE_CLIENT_ID
- your multi-tenant app registration's app IDAZURE_CLIENT_SECRET
- a client secret for your multi-tenant app registrationNote: In general, it is better to use a certificate than a secret.
Instead of using DefaultAzureCredential, you can directly create a ClientSecretCredential or a ClientCertificateCredential. You'll need to store the credential somewhere safe.
For example, on approach you could follow which would avoid any credentials in environment variable or in code:
https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme
Upvotes: 2
Reputation: 58823
Managed Identities can only get tokens within the tenant that they exist in. You'd need a "traditional" multi-tenant app registration + client certificate/secret for this case.
Upvotes: 1