Describe the bug
As reported in Azure/AppConfiguration#201, it seems that using SharedTokenCacheCredential (which extends to DefaultAzureCredential) doesn't work when the user is part of multiple tenants.
Last week AzureAppConfiguration was updated to version 3, and it started using Azure.Identity. Version 2 seems to have used a self-implemented way of connecting with .ConnectWithManagedIdentity that has a fallback to local credentials if managed identity is not available. This worked just fine. Version 3 however, fails.
If I don't set SharedTokenCacheTenantId, it fails with a 500 (which is a bug apparently and should return 401). If I do set SharedTokenCacheTenantId, it fails with the message that no accounts have been found matching the tenant id. However, I am part of the tenant that I'm trying to connect to.
Exception or Stack Trace
SharedTokenCacheCredential is unavailable
No account matching the specified tenantId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx was found in the cache.
To authenticate with the SharedTokenCacheCredential, login an account through developer tooling supporting Azure single sign on.
[ {username: ****** tenantId: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy} ].
To Reproduce
In my case, I have a home tenant A and I am a guest in tenant B.
The App Configuration instance that I want to connect to from my application resides in tenant B.
Executing the code fails with the above mentioned message.
Code Snippet
var builder = new ConfigurationBuilder();
builder.AddAzureAppConfiguration(x =>
{
x.Connect(new Uri("https://xxxx.azconfig.io"), new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
SharedTokenCacheTenantId = "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx",
}));
});
var config = builder.Build();
Expected behavior
I'm expecting this not to fail. Additionally, I expect it to succeed even without having to manually set the tenant id as it does with version 2 of AppConfiguration.
Setup (please complete the following information):
Information Checklist
Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report
cc @schaabs
@b10-dslappendel thanks for reporting this. We are aware of this issue and are working on a fix for it and we will release it early next year.
@AlexGhiondea any update here? I'm trying to use DefaultAzureCredential to fall back to SharedTokenCacheCredential while logged in to multiple tenants/subscriptions. I have specified AZURE_USERNAME and AZURE_TENANT_ID in the environment variables but still get the error message that my username is not found in the tenant.
In my case I have a Function App which utilizes the Storage SDK with a Managed Identity when on Azure, but need this to work in a local dev environment and I was expecting to get this functionality by using DefaultAzureCredential
@brandonh-msft Just as something else to try.. make sure your username is exact case as what is shown in VS.
I had a similar problem with multiple accounts and account not being found, and it was down to the username check in Shared Token Cache being case sensitive.
@brandonh-msft And if you're using an aliased Microsoft Account, this could also be a problem.
@arkiaconsulting just the fact that an aliased microsoft account/AAD account combination exists or having them both logged in?
We ended up solving the problem by setting the includeInteractive boolean on the DefaultAzureCredential ctor to true. This pops a browser when we hit the code in either VS or VSCode, but does not work in VSCode DevContainers - I'm working on opening a thread w/ that team now.
Setting AZURE_TENANT_ID is required in order to disambiguate between tenants, and furthermore AZURE_SUBSCRIPTION_ID to further disambig between the multiple subs we have access to in the tenant, but is a palatable _local_ dev experience for us.
The problem is :
In file https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/src/SharedTokenCacheCredential.cs, line136, you have this code that is used to find your account :
List<IAccount> filteredAccounts = accounts.Where(a => (string.IsNullOrEmpty(_username) || a.Username == _username) && (string.IsNullOrEmpty(_tenantId) || a.HomeAccountId?.TenantId == _tenantId)).ToList();
So the filtering is done using the HomeAccountId. However, this ID is always the tenant ID of the main tenant of a user. If you want to log on a tenant you are guest into, it won't work. I have checked the MSAL token cache file "%HOMEPATH%\AppData\Local.IdentityService\AccountStore.json", and in my case the ID of the tenant I want to log on is in the JSON property IdentityServiceTenants, which is a serialized json array. But I noticed that the IAccount interface that is used by SharedTokenCacheCredential does not expose this property.
So I don't think this can work with the actual design.
As @brandonh-msft said, an actual workaround is to use the interactive browser and specifying the tenant id. We use this workaround on my projet since 3 month now. But it's a clearly a workaround and not solution since you need to relog on the interactive browser each time you launch your application which is really annoying.
@sopelt I experienced the issue with a external developer having an Microsoft Account having a main hotmail email, and an outlook alias. We invited him in our tenant with his alias, and so he's not able to use SharedTokenCacheCredential.
As a workaround, I think we will use a service principal locally, and fall back to EnvironmentCredential.
The problem is :
In file https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/src/SharedTokenCacheCredential.cs, line136, you have this code that is used to find your account :List<IAccount> filteredAccounts = accounts.Where(a => (string.IsNullOrEmpty(_username) || a.Username == _username) && (string.IsNullOrEmpty(_tenantId) || a.HomeAccountId?.TenantId == _tenantId)).ToList();
Thanks for finding & calling this out. I pulled down the source and poked around; it doesn't look like there's a list of all tenants that we could even filter on at the point where we're filtering the accounts :/ I went up in to the MsalPublicClient and still no _list_ of tenant IDs for us to look for.
This is going to require some more significant work; cc'ing @schaabs as he's the one whose name's on the last commits to the area :)
Thank you all for you're contributions to this issue. As @a99cl208 and @brandonh-msft pointed out the MSAL APIs don't expose guest tenant information when we list accounts, so at this time the SharedTokenCacheCredential is limited in it's functionality because of this. We are working on broader changes to address this, which should be coming in our next preview release in May.
Also, in our last preview release, 1.2.0-preview.2, we added the VisualStudioCredential to the DefaultAzureCredential authentication flow, which I believe will unblock this scenario of signing into a guest tenant when authenticating in VS. If you log into VS with the desired account, and then specify the tenant you wish to authenticate in either via the AZURE_TENANT_ID environment variable or the VisualStudioTenantId property on DefaultAzureCredentialOptions this will authenticate the account to the desired tenant.
@bradrich-msft where can I follow progress on using an interactive login from a devcontainer?
@bradrich-msft where can I follow progress on using an interactive login from a devcontainer?
I think you may have the wrong person. I have no idea what you are referring to.
@bradrich-msft sorry. you are right:
@brandonh-msft where can I follow progress on using an interactive login from a devcontainer?
@jabbera I opened this stackoverflow question on the issue but as of yet it's seen no answers. @schaabs would you like me to convert this SO q to an issue on the SDK here? (cc @jongio )
@brandonh-msft selfishly I’m looking for the python sdk but considering this required a collaboration with the remote dev team I was hoping the solution would be the same. 🤷♂️
Thank you all for you're contributions to this issue. As @a99cl208 and @brandonh-msft pointed out the MSAL APIs don't expose guest tenant information when we list accounts, so at this time the
SharedTokenCacheCredentialis limited in it's functionality because of this. We are working on broader changes to address this, which should be coming in our next preview release in May.Also, in our last preview release, 1.2.0-preview.2, we added the
VisualStudioCredentialto theDefaultAzureCredentialauthentication flow, which I believe will unblock this scenario of signing into a guest tenant when authenticating in VS. If you log into VS with the desired account, and then specify the tenant you wish to authenticate in either via the AZURE_TENANT_ID environment variable or theVisualStudioTenantIdproperty onDefaultAzureCredentialOptionsthis will authenticate the account to the desired tenant.
@schaabs what's the progress on this?
I see that there have been some updates, and testing the latest changes in preview 5 I am able to authenticate with VisualStudioCredential as long as I specify TenantId. If I use DefaultAzureCredential with VisualStudioTenantId however, it gives me a 401. And using it without specifying any tenant id results in the same 401.
While I can make it working now regarding my issue in https://github.com/Azure/AppConfiguration/issues/201, it's still a regression that I have to specify a tenant id.
Most helpful comment
Thank you all for you're contributions to this issue. As @a99cl208 and @brandonh-msft pointed out the MSAL APIs don't expose guest tenant information when we list accounts, so at this time the
SharedTokenCacheCredentialis limited in it's functionality because of this. We are working on broader changes to address this, which should be coming in our next preview release in May.Also, in our last preview release, 1.2.0-preview.2, we added the
VisualStudioCredentialto theDefaultAzureCredentialauthentication flow, which I believe will unblock this scenario of signing into a guest tenant when authenticating in VS. If you log into VS with the desired account, and then specify the tenant you wish to authenticate in either via the AZURE_TENANT_ID environment variable or theVisualStudioTenantIdproperty onDefaultAzureCredentialOptionsthis will authenticate the account to the desired tenant.