[BUG] Azure.Identity 1.4- SharedTokenCacheCredential no longer works when used in DefaultAzureCredentialOptions
See original GitHub issueLibrary name and version
Azure.Identity 1.5
Describe the bug
Pardon the title gore.
This commit https://github.com/Azure/azure-sdk-for-net/commit/215a57637d0b115b4231c47b7df37452ff0772a2 changed how we hook up TokenCache:
The effect is that it broke silent authentication from token cache that was working with Azure.Identity 1.3
Here is the code:
const string akv = "https://MyKeyVault.vault.azure.net/";
string secretName = "MySecret";
const string MicrosoftTenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47";
DefaultAzureCredentialOptions credentialOptions = new()
{
// We only want Shared Token Cache and Interactive credential for MFA authentication
ExcludeSharedTokenCacheCredential = false, // <-- no longer works with 1.5, always falls through to next credential in the chain
ExcludeInteractiveBrowserCredential = false,
// We explicitly exclude all other credential types.
ExcludeAzureCliCredential = true,
ExcludeEnvironmentCredential = true,
ExcludeManagedIdentityCredential = true,
ExcludeVisualStudioCodeCredential = true,
ExcludeVisualStudioCredential = true,
ExcludeAzurePowerShellCredential = true,
// Resources are in Microsoft tenant (as opposed to say AME).
// Making it explicit just in case (to prevent potential conflict with user having authenticated to another tenant)
InteractiveBrowserTenantId = MicrosoftTenantId,
SharedTokenCacheTenantId = MicrosoftTenantId,
};
TokenCredential userCredentials = new DefaultAzureCredential(credentialOptions);
SecretClient sc = new SecretClient(new Uri(akv), userCredentials);
KeyVaultSecret kvs = sc.GetSecret(secretName, version: null, CancellationToken.None); // <-- prompts once with 1.3; always prompts in 1.5. If you inspect private properties of userCredentials, only in 1.3 SharedTokenCacheCredential is used.
What I found in debugger is SharedTokenCacheCredential not able to use cache because TokenCache is null below, hence it does not find any cached accounts and passes control to the next credential in chain:
//C:\Users\ayeltsov\AppData\Local\SourceServer\ec994ec8e5e8a8720a9c1a17f5a92aeb5df39eca966203725c5cc67aadc2988b\sdk\identity\Azure.Identity\src\MsalClientBase.cs
protected async ValueTask<TClient> GetClientAsync(bool async, CancellationToken cancellationToken)
{
using var asyncLock = await _clientAsyncLock.GetLockOrValueAsync(async, cancellationToken).ConfigureAwait(false);
if (asyncLock.HasValue)
{
return asyncLock.Value;
}
var client = await CreateClientAsync(async, cancellationToken).ConfigureAwait(false);
if (TokenCache != null) // <-- this is null
{
await TokenCache.RegisterCache(async, client.UserTokenCache, cancellationToken).ConfigureAwait(false);
if (client is IConfidentialClientApplication cca)
{
await TokenCache.RegisterCache(async, cca.AppTokenCache, cancellationToken).ConfigureAwait(false);
}
}
asyncLock.SetValue(client);
return client;
}
I did not find how to initialize DefaultAzureCredentialOptions / DefaultAzureCredential so that TokenCache for SharedTokenCacheCredential gets populated.
Am I missing anything? Even if I do please note this is a breaking change resulting in authentication prompt being presented to the user every time they run the tool.
Expected behavior
Prompt for credentials once, then use cached credentials
Actual behavior
Prompt for credentials every time
Reproduction Steps
See description
Environment
.NET SDK (reflecting any global.json): Version: 6.0.103 Commit: 2d7bc7059f
Runtime Environment: OS Name: Windows OS Version: 10.0.22000 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\6.0.103\
Host (useful for support): Version: 6.0.3 Commit: c24d9a9c91
.NET SDKs installed: 3.1.417 [C:\Program Files\dotnet\sdk] 5.0.406 [C:\Program Files\dotnet\sdk] 6.0.103 [C:\Program Files\dotnet\sdk]
.NET runtimes installed: Microsoft.AspNetCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.23 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET runtimes or SDKs: https://aka.ms/dotnet-download
Issue Analytics
- State:
- Created a year ago
- Comments:7 (4 by maintainers)
Top GitHub Comments
Actually I’m saying that workaround I used in https://github.com/Azure/azure-sdk-for-net/issues/23896#issuecomment-1085047864 works across application runs and multiple applications without having to worry about AuthenticationRecord
Yes, you only need to worry about the
AuthenticationRecord
if you need the credentials to persist across runs of the application, or across multiple applications using the same credentials.