Credential chains in the Azure Identity client library for Go

The Azure Identity client library provides credentials—public types that implement the Azure Core library's TokenCredential interface. A credential represents a distinct authentication flow for acquiring an access token from Microsoft Entra ID. These credentials can be chained together to form an ordered sequence of authentication mechanisms to be attempted.

How a chained credential works

At runtime, a credential chain attempts to authenticate using the sequence's first credential. If that credential fails to acquire an access token, the next credential in the sequence is attempted, and so on, until an access token is successfully obtained. The following sequence diagram illustrates this behavior:

Diagram that shows credential chain sequence.

Why use credential chains

A chained credential can offer the following benefits:

  • Environment awareness: Automatically selects the most appropriate credential based on the environment in which the app is running. Without it, you'd have to write code like this:

    // Set up credential based on environment (Azure or local development)
    if os.Getenv("WEBSITE_HOSTNAME") != "" {
        clientID := azidentity.ClientID("abcd1234-...")
        opts := azidentity.ManagedIdentityCredentialOptions{ID: clientID}
        cred, err := azidentity.NewManagedIdentityCredential(&opts)
    
        if err != nil {
          // TODO: handle error
        }
    }
    else {
        // Use Azure CLI Credential
        credential, err = azidentity.NewAzureCLICredential(nil)
    
        if err != nil {
          // TODO: handle error
        }
    }
    
  • Seamless transitions: Your app can move from local development to your staging or production environment without changing authentication code.

  • Improved resiliency: Includes a fallback mechanism that moves to the next credential when the prior fails to acquire an access token.

How to choose a chained credential

With Go, there are two choices for credential chaining:

  • Use a preconfigured chain: Use the preconfigured chain implemented by the DefaultAzureCredential type. For this approach, see the DefaultAzureCredential overview section.
  • Build a custom credential chain: Start with an empty chain and include only what you need. For this approach, see the ChainedTokenCredential overview section.

DefaultAzureCredential overview

DefaultAzureCredential is an opinionated, preconfigured chain of credentials. It's designed to support many environments, along with the most common authentication flows and developer tools. In graphical form, the underlying chain looks like this:

Diagram that shows DefaultAzureCredential authentication flow.

The order in which DefaultAzureCredential attempts credentials follows.

Order Credential Description
1 Environment Reads a collection of environment variables to determine if an application service principal (application user) is configured for the app. If so, DefaultAzureCredential uses these values to authenticate the app to Azure. This method is most often used in server environments but can also be used when developing locally.
2 Workload Identity If the app is deployed to an Azure host with Workload Identity enabled, authenticate that account.
3 Managed Identity If the app is deployed to an Azure host with Managed Identity enabled, authenticate the app to Azure using that Managed Identity.
4 Azure CLI If the developer authenticated to Azure using Azure CLI's az login command, authenticate the app to Azure using that same account.
5 Azure Developer CLI If the developer authenticated to Azure using Azure Developer CLI's azd auth login command, authenticate with that account.

In its simplest form, you can use the parameterless version of DefaultAzureCredential as follows:

import (
    "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
    "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
    )

// create a credential
credential, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
    // TODO: handle error
}

// create a Blob service client 
accountURL := "https://<my_account_name>.blob.core.windows.net"
client, err := azblob.NewClient(accountURL, credential, nil)
if err != nil {
    // TODO: handle error
}

ChainedTokenCredential overview

ChainedTokenCredential is an empty chain to which you add credentials to suit your app's needs. For example:

managed, err := azidentity.NewManagedIdentityCredential(nil)
if err != nil {
  // handle error
}

azCLI, err := azidentity.NewAzureCLICredential(nil)
if err != nil {
  // handle error
}

chain, err := azidentity.NewChainedTokenCredential([]azcore.TokenCredential{managed, azCLI}, nil)
if err != nil {
  // handle error
}

The preceding code sample creates a tailored credential chain comprised of two credentials. ManagedIdentityCredential is attempted first, followed by AzureCliCredential, if necessary. In graphical form, the chain looks like this:

Diagram that shows authentication flow for a ChainedTokenCredential instance that is composed of managed identity credential and Azure CLI credential.

Tip

For improved performance, optimize credential ordering in ChainedTokenCredential for your production environment. Credentials intended for use in the local development environment should be added last.

Usage guidance for DefaultAzureCredential

DefaultAzureCredential is undoubtedly the easiest way to get started with the Azure Identity client library, but with that convenience comes tradeoffs. Once you deploy your app to Azure, you should understand the app's authentication requirements. For that reason, strongly consider moving from DefaultAzureCredential to one of the following solutions:

  • A specific credential implementation, such as ManagedIdentityCredential.
  • A pared-down ChainedTokenCredential implementation optimized for the Azure environment in which your app runs.

Here's why:

  • Debugging challenges: When authentication fails, it can be challenging to debug and identify the offending credential. You must enable logging to see the progression from one credential to the next and the success/failure status of each. For more information, see Debug a chained credential.
  • Performance overhead: The process of sequentially trying multiple credentials can introduce performance overhead. For example, when running on a local development machine, managed identity is unavailable. Consequently, ManagedIdentityCredential always fails in the local development environment, unless explicitly disabled via its corresponding exclude-prefixed property.
  • Unpredictable behavior: DefaultAzureCredential checks for the presence of certain environment variables. It's possible that someone could add or modify these environment variables at the system level on the host machine. Those changes apply globally and therefore alter the behavior of DefaultAzureCredential at runtime in any app running on that machine.

Debug a chained credential

To diagnose an unexpected issue or to understand what a chained credential is doing, enable logging in your app. Optionally, filter the logs to only those events emitted from the Azure Identity client library. For example:

import azlog "github.com/Azure/azure-sdk-for-go/sdk/azcore/log"
// print log output to stdout
azlog.SetListener(func(event azlog.Event, s string) {
    fmt.Println(s)
})
// include only azidentity credential logs
azlog.SetEvents(azidentity.EventAuthentication)

For guidance on resolving errors from specific credential types, see the troubleshooting guide.