mrkeyoor.com_
Thu 06 Aug 01:04 UTC
PyPIInfraupdated 05 Aug 2026

azure-identity

azure-identity is the token layer that every other Azure SDK package for Python plugs into. It gives you credential objects that know how to obtain a Microsoft Entra ID access token in a specific way: from environment variables, from a managed identity on an Azure host, from a Kubernetes workload identity file, from a service principal secret or certificate, or from whatever you last signed in with using the Azure CLI. Every Azure client takes one of these objects in its constructor and calls get_token when it needs to. The headline class, DefaultAzureCredential, tries a fixed chain of the others in order so the same code can run on your laptop and in production without a branch.

Verdict

The correct and effectively mandatory way to authenticate Azure SDK clients from Python, and managed identity support here is the main reason production code can hold zero secrets. Use DefaultAzureCredential while developing, then pin production to the one credential you actually intend to use.

API stability5/5The TokenCredential protocol and the credential class names have held since 1.0, and additions such as WorkloadIdentityCredential and the AZURE_TOKEN_CREDENTIALS switch arrived as new options rather than replacements. Behavior inside DefaultAzureCredential has shifted, notably the 1.14 change that lets the chain continue past a failing developer credential.
Docs5/5The README tables every credential class with when to use it, plus environment variables, cloud configuration, caching, and error handling, and Microsoft Learn adds a dedicated troubleshooting guide keyed to the error text. The repository also keeps a design decision log explaining why the chain behaves the way it does.
Maintenance4/5Microsoft maintains it inside the azure-sdk-for-python monorepo, which saw commits on the day of this review. Stable releases are slower than the repo suggests: 1.25.3 landed in March 2026 and the 1.26 line has been in beta since February. The 795 open issues on the tracker span every package in the monorepo.
Ecosystem5/5About 58M weekly downloads and a hard dependency of essentially every Azure SDK package, so credentials are interchangeable across clients. Extensions exist for the Windows broker and for persistent caching.

Use it if

  • You call any Azure SDK client from Python (Key Vault, Blob Storage, Service Bus, Cosmos, resource management): they all accept a TokenCredential and this is the package that provides one
  • You run on App Service, Functions, Container Apps, AKS, or a VM and want managed identity so there is no secret in your config at all
  • You want local development to authenticate as the developer via az login while the deployed build authenticates as its workload identity, from the same source file
  • You need Entra tokens outside the SDK too: get_bearer_token_provider hands Azure OpenAI clients a refreshing token, and get_token covers direct REST calls
Skip it if

Setup reality

pip install azure-identity needs Python 3.9 or newer and brings azure-core, msal, msal-extensions, cryptography, and typing-extensions with it. The install is quick; the environment is the work. Locally you first sign in with az login, azd auth login, or VS Code, and DefaultAzureCredential picks whichever it finds, so two developers can get different identities from identical code. For a user-assigned managed identity you must pass the client id explicitly, because the default asks for the system-assigned one and a host with several identities returns a token you did not want. Sovereign clouds need the authority argument or AZURE_AUTHORITY_HOST. Async needs pip install aiohttp on top. When something fails you get a single ClientAuthenticationError whose message concatenates the failure of every credential in the chain, which is long, and the useful line is rarely the first. Tokens are cached in memory by default and only persist to disk if you opt in with TokenCachePersistenceOptions.

Patterns

Authenticate an SDK clientdefault-credential

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()
client = SecretClient(
    vault_url="https://my-vault.vault.azure.net",
    credential=credential,
)

secret = client.get_secret("db-password")

Build one credential and share it across clients. It caches tokens in memory, so a new instance per request throws away the cache and hits Entra ID again.

Narrow the chain for productionconstrain-chain

# option 1: environment switch, no code change
#   AZURE_TOKEN_CREDENTIALS=prod   (deployed credentials only)
#   AZURE_TOKEN_CREDENTIALS=dev    (developer tools only)
#   AZURE_TOKEN_CREDENTIALS=managedidentitycredential

# option 2: exclude explicitly
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential(
    exclude_cli_credential=True,
    exclude_powershell_credential=True,
    exclude_developer_cli_credential=True,
    exclude_visual_studio_code_credential=True,
)

This is the single highest-value change to make before deploying. It removes the class of incident where a container inherits a developer identity, and it cuts the probe timeouts out of cold start.

Use a specific managed identityuser-assigned-managed-identity

from azure.identity import ManagedIdentityCredential

# user-assigned: the client id is required
credential = ManagedIdentityCredential(
    client_id="11111111-2222-3333-4444-555555555555"
)

# system-assigned: no arguments
# credential = ManagedIdentityCredential()

On a host with more than one user-assigned identity, omitting client_id gets you a token for whichever the platform considers default, which typically surfaces as a 403 from the target service rather than an auth error.

Authenticate a service principal with a secretservice-principal

from azure.identity import ClientSecretCredential

credential = ClientSecretCredential(
    tenant_id=os.environ["AZURE_TENANT_ID"],
    client_id=os.environ["AZURE_CLIENT_ID"],
    client_secret=os.environ["AZURE_CLIENT_SECRET"],
)

Setting those same three variables and using EnvironmentCredential or DefaultAzureCredential gets you the identical result with no secret in the code path. Prefer managed identity or a certificate where the platform allows it.

Authenticate a pod with workload identity on AKSworkload-identity

from azure.identity import WorkloadIdentityCredential

credential = WorkloadIdentityCredential(
    tenant_id=os.environ["AZURE_TENANT_ID"],
    client_id=os.environ["AZURE_CLIENT_ID"],
    token_file_path=os.environ["AZURE_FEDERATED_TOKEN_FILE"],
)

Those three variables are injected by the AKS workload identity webhook, so DefaultAzureCredential finds this path on its own. Instantiate it directly when you want a failure to be loud instead of falling through to the next credential.

Build your own fallback orderchained-credential

from azure.identity import (
    ChainedTokenCredential,
    ManagedIdentityCredential,
    AzureCliCredential,
)

credential = ChainedTokenCredential(
    ManagedIdentityCredential(client_id=os.environ["UAMI_CLIENT_ID"]),
    AzureCliCredential(),
)

Two entries you chose beats the ten-step default chain: the order is explicit, the failure message is short, and there is no path to an identity you did not intend.

Use async credentials correctlyasync-credential

from azure.identity.aio import DefaultAzureCredential
from azure.keyvault.secrets.aio import SecretClient

async with DefaultAzureCredential() as credential:
    async with SecretClient(vault_url, credential) as client:
        secret = await client.get_secret("db-password")

azure.identity.aio needs an async transport installed separately, usually aiohttp. Credentials hold an HTTP session, so close them or use the context manager; creating one per request without closing leaks connections.

Fetch a raw token for your own HTTP callget-token-directly

import requests
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
token = credential.get_token("https://management.azure.com/.default")

resp = requests.get(
    "https://management.azure.com/subscriptions?api-version=2022-12-01",
    headers={"Authorization": f"Bearer {token.token}"},
)

Scopes end in /.default for application permissions and the resource must match the API you are calling. token.expires_on is a POSIX timestamp; the credential caches internally, so calling get_token again is cheap rather than a new network round trip.

Authenticate Azure OpenAI without a keybearer-token-provider

from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from openai import AzureOpenAI

token_provider = get_bearer_token_provider(
    DefaultAzureCredential(),
    "https://cognitiveservices.azure.com/.default",
)

client = AzureOpenAI(
    azure_endpoint="https://my-resource.openai.azure.com",
    azure_ad_token_provider=token_provider,
    api_version="2024-10-21",
)

The provider is a callable that returns a fresh token string on each call, so refresh is handled for you. The identity still needs the Cognitive Services OpenAI User role on the resource.

Tell a missing credential from a rejected oneerror-handling

from azure.identity import CredentialUnavailableError
from azure.core.exceptions import ClientAuthenticationError

try:
    token = credential.get_token("https://vault.azure.net/.default")
except CredentialUnavailableError:
    # nothing to try with: no env vars, no signed-in CLI, no IMDS
    raise
except ClientAuthenticationError as e:
    # Entra ID said no: wrong tenant, expired secret, no consent
    log.error("auth failed: %s", e.message)
    raise

CredentialUnavailableError is what lets the chain move on to the next credential, so a chained credential only raises it when every link was unavailable. ClientAuthenticationError from a chain concatenates each link's message, and the actionable one is usually near the end.

Persist the token cache across restartspersistent-token-cache

from azure.identity import (
    InteractiveBrowserCredential,
    TokenCachePersistenceOptions,
)

credential = InteractiveBrowserCredential(
    cache_persistence_options=TokenCachePersistenceOptions(name="my-cli"),
)

Caching is in memory only unless you opt in like this. It is worth it for CLI tools so users do not sign in on every invocation, and it uses the OS keyring, which needs a working secret service on headless Linux.

Target a non-public cloudsovereign-cloud

from azure.identity import AzureAuthorityHosts, DefaultAzureCredential

credential = DefaultAzureCredential(
    authority=AzureAuthorityHosts.AZURE_GOVERNMENT
)

# or, for every credential at once:
#   AZURE_AUTHORITY_HOST=https://login.partner.microsoftonline.cn

Credentials backed by a developer tool, such as AzureCliCredential, ignore this and follow whatever cloud that tool is configured for, so a mixed setup can authenticate against two clouds at once.

Alternatives

PackageRegistryPick it when
msalPyPIYou want raw Entra token acquisition without the credential chain abstraction, and you are not using Azure SDK clients that expect a TokenCredential
azure-identity-brokerPyPIYou build a Windows desktop app and want brokered sign-in through the Web Account Manager instead of a browser redirect
azure-cliPyPIYour automation is a script in CI that can shell out to az and read a token from it rather than embedding a credential library