Authentication best practices with the Azure Identity library for .NET
This article offers guidelines to help you maximize the performance and reliability of your .NET apps when authenticating to Azure services. To make the most of the Azure Identity library for .NET, it's important to understand potential issues and mitigation techniques.
Use deterministic credentials in production environments
DefaultAzureCredential
is the most approachable way to get started with the Azure Identity library, but that convenience also introduces certain tradeoffs. Most notably, the specific credential in the chain that will succeed and be used for request authentication can't be guaranteed ahead of time. In a production environment, this unpredictability can introduce significant and sometimes subtle problems.
For example, consider the following hypothetical sequence of events:
- An organization's security team mandates all apps use managed identity to authenticate to Azure resources.
- For months, a .NET app hosted on an Azure Virtual Machine (VM) successfully uses
DefaultAzureCredential
to authenticate via managed identity. - Without telling the support team, a developer installs the Azure CLI on that VM and runs the
az login
command to authenticate to Azure. - Due to a separate configuration change in the Azure environment, authentication via the original managed identity unexpectedly begins to fail silently.
DefaultAzureCredential
skips the failedManagedIdentityCredential
and searches for the next available credential, which isAzureCliCredential
.- The application starts utilizing the Azure CLI credentials rather than the managed identity, which may fail or result in unexpected elevation or reduction of privileges.
To prevent these types of subtle issues or silent failures in production apps, strongly consider moving from DefaultAzureCredential
to one of the following deterministic solutions:
- A specific
TokenCredential
implementation, such asManagedIdentityCredential
. See the Derived list for options. - A pared-down
ChainedTokenCredential
implementation optimized for the Azure environment in which your app runs.ChainedTokenCredential
essentially creates a specific allowlist of acceptable credential options, such asManagedIdentity
for production andVisualStudioCredential
for development.
For example, consider the following DefaultAzureCredential
configuration in an ASP.NET Core project:
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(
new Uri("https://<account-name>.blob.core.windows.net"));
DefaultAzureCredential credential = new();
clientBuilder.UseCredential(credential);
});
Replace the preceding code with a ChainedTokenCredential
implementation that specifies only the necessary credentials:
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddBlobServiceClient(
new Uri("https://<account-name>.blob.core.windows.net"));
clientBuilder.UseCredential(new ChainedTokenCredential(
new ManagedIdentityCredential(clientId: userAssignedClientId),
new VisualStudioCredential()));
});
In this example, ManagedIdentityCredential
would be automatically discovered in production, while VisualStudioCredential
would work in local development environments.
Reuse credential instances
Reuse credential instances when possible to improve app resilience and reduce the number of access token requests issued to Microsoft Entra ID. When a credential is reused, an attempt is made to fetch a token from the app token cache managed by the underlying MSAL dependency. For more information, see Token caching in the Azure Identity client library.
Important
A high-volume app that doesn't reuse credentials may encounter HTTP 429 throttling responses from Microsoft Entra ID, which can lead to app outages.
The recommended credential reuse strategy differs by .NET application type.
Implement credential reuse through the UseCredential method of Microsoft.Extensions.Azure
. For example, imagine an ASP.NET Core app hosted on Azure App Service, with a UserAssignedClientId
environment variable set. The .NET configuration provider determines the environment variable exists, and ManagedIdentityCredential
will be used to authenticate the Key Vault Secrets and Blob Storage clients. Otherwise, a chained sequence of development-time credentials is used.
builder.Services.AddAzureClients(clientBuilder =>
{
clientBuilder.AddSecretClient(new Uri("<key-vault-url>"));
clientBuilder.AddBlobServiceClient(new Uri("<blob-storage-url>"));
string? clientId = builder.Configuration["UserAssignedClientId"];
TokenCredential credential = clientId is not null
? new ManagedIdentityCredential(
ManagedIdentityId.FromUserAssignedClientId(clientId))
: new ChainedTokenCredential(
new VisualStudioCredential(),
new AzureCliCredential(),
new AzurePowerShellCredential());
clientBuilder.UseCredential(credential);
});
For information on this approach, see Authenticate using Microsoft Entra ID.
Understand when token lifetime and caching logic is needed
If you use an Azure Identity library credential outside the context of an Azure SDK client library that depends on Azure Core, it becomes your responsibility to manage token lifetime and caching behavior in your app.
The RefreshOn property on AccessToken
, which provides a hint to consumers as to when token refresh can be attempted, will be automatically used by Azure SDK client libraries that depend on the Azure Core library to refresh the token. For direct usage of Azure Identity library credentials that support token caching, the underlying MSAL cache automatically refreshes proactively when the RefreshOn
time occurs. This design allows the client code to call GetToken
each time a token is needed and delegate the refresh to the library.
To only call GetToken
when necessary, observe the RefreshOn
date and proactively attempt to refresh the token after that time. The specific implementation is up to the customer.
Understand the managed identity retry strategy
The Azure Identity library for .NET allows you to authenticate via managed identity with ManagedIdentityCredential
. The way in which you use ManagedIdentityCredential
impacts the applied retry strategy. When used via:
DefaultAzureCredential
, no retries are attempted when the initial token acquisition attempt fails or times out after a short duration. This is the least resilient option because it's optimized to "fail fast" for an efficient development inner loop.- Any other approach, such as
ChainedTokenCredential
orManagedIdentityCredential
directly:The time interval between retries starts at 0.8 seconds, and a maximum of five retries are attempted, by default. This option is optimized for resilience but introduces potentially unwanted delays in the development inner loop.
To change any of the default retry settings, use the
Retry
property onManagedIdentityCredentialOptions
. For example, retry a maximum of three times, with a starting interval of 0.5 seconds:ManagedIdentityCredentialOptions miCredentialOptions = new( ManagedIdentityId.FromUserAssignedClientId(clientId) ) { Retry = { MaxRetries = 3, Delay = TimeSpan.FromSeconds(0.5), } }; ChainedTokenCredential tokenChain = new( new ManagedIdentityCredential(miCredentialOptions), new VisualStudioCredential() );
For more information on customizing retry policies, see Setting a custom retry policy.