Edit

Share via


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:

  1. An organization's security team mandates all apps use managed identity to authenticate to Azure resources.
  2. For months, a .NET app hosted on an Azure Virtual Machine (VM) successfully uses DefaultAzureCredential to authenticate via managed identity.
  3. Without telling the support team, a developer installs the Azure CLI on that VM and runs the az login command to authenticate to Azure.
  4. Due to a separate configuration change in the Azure environment, authentication via the original managed identity unexpectedly begins to fail silently.
  5. DefaultAzureCredential skips the failed ManagedIdentityCredential and searches for the next available credential, which is AzureCliCredential.
  6. 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, replace DefaultAzureCredential with a specific TokenCredential implementation, such as ManagedIdentityCredential. See the Derived list for options.

For example, consider the following DefaultAzureCredential configuration in an ASP.NET Core project:

C#
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddSecretClient(
        new Uri($"https://{keyVaultName}.vault.azure.net"));
    clientBuilder.AddBlobServiceClient(
        new Uri($"https://{storageAccountName}.blob.core.windows.net"));

    DefaultAzureCredential credential = new();
    clientBuilder.UseCredential(credential);
});

Modify the preceding code to select a credential based on the environment in which the app is running:

C#
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddSecretClient(
        new Uri($"https://{keyVaultName}.vault.azure.net"));
    clientBuilder.AddBlobServiceClient(
        new Uri($"https://{storageAccountName}.blob.core.windows.net"));

    TokenCredential credential;

    if (builder.Environment.IsProduction() || builder.Environment.IsStaging())
    {
        string? clientId = builder.Configuration["UserAssignedClientId"];
        credential = new ManagedIdentityCredential(
            ManagedIdentityId.FromUserAssignedClientId(clientId));
    }
    else
    {
        // local development environment
        credential = new ChainedTokenCredential(
            new VisualStudioCredential(),
            new AzureCliCredential(),
            new AzurePowerShellCredential());
    }

    clientBuilder.UseCredential(credential);
});

In this example, only ManagedIdentityCredential is used in production. The local development environment's authentication needs are then serviced by the sequence of credentials defined in the else clause.

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.

To implement credential reuse, use the UseCredential method from Microsoft.Extensions.Azure. Consider an ASP.NET Core app hosted on Azure App Service in both production and staging environments. Environment variable ASPNETCORE_ENVIRONMENT is set to either Production or Staging to differentiate between these two non-development environments. In both production and staging, the user-assigned variant of ManagedIdentityCredential is used to authenticate the Key Vault Secrets and Blob Storage clients.

When the app runs on a local development machine, where ASPNETCORE_ENVIRONMENT is set to Development, a chained sequence of developer tool credentials is used instead. This approach ensures environment-appropriate credentials are used, enhancing security and simplifying credential management.

C#
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddSecretClient(
        new Uri($"https://{keyVaultName}.vault.azure.net"));
    clientBuilder.AddBlobServiceClient(
        new Uri($"https://{storageAccountName}.blob.core.windows.net"));

    TokenCredential credential;

    if (builder.Environment.IsProduction() || builder.Environment.IsStaging())
    {
        string? clientId = builder.Configuration["UserAssignedClientId"];
        credential = new ManagedIdentityCredential(
            ManagedIdentityId.FromUserAssignedClientId(clientId));
    }
    else
    {
        // local development environment
        credential = new ChainedTokenCredential(
            new VisualStudioCredential(),
            new AzureCliCredential(),
            new AzurePowerShellCredential());
    }

    clientBuilder.UseCredential(credential);
});

For information on this approach in an ASP.NET Core app, 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 or ManagedIdentityCredential 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 on ManagedIdentityCredentialOptions. For example, retry a maximum of three times, with a starting interval of 0.5 seconds:

      C#
      ManagedIdentityCredentialOptions miCredentialOptions = new(
              ManagedIdentityId.FromUserAssignedClientId(clientId)
          )
          {
              Retry =
              {
                  MaxRetries = 3,
                  Delay = TimeSpan.FromSeconds(0.5),
              }
          };
      
      ManagedIdentityCredential miCredential = new(miCredentialOptions);
      

For more information on customizing retry policies, see Setting a custom retry policy.