适用于 JavaScript 的 Azure 标识库中的凭据链

Azure 标识库提供 凭据—实现 Azure Core 库的 TokenCredential 接口的公共类。 凭据表示从 Microsoft Entra ID 获取访问令牌的独特身份验证流。 这些凭据可以链接在一起,形成要尝试的有序身份验证机制序列。

链式凭证的工作原理

在运行时,凭据链尝试使用序列的第一个凭据进行身份验证。 如果该凭据无法获取访问令牌,则会尝试序列中的下一个凭据,依此方式,直到成功获取访问令牌。 以下序列图说明了此行为:

凭据链序列图,其中显示了在获取访问令牌之前,身份验证尝试通过多个凭据进行。

为何使用凭据链

链式凭证可以提供以下优势:

  • 环境感知:根据应用运行的环境自动选择最合适的凭据。 如果没有它,必须编写如下所示的代码:

    import { 
        ManagedIdentityCredential, 
        AzureCliCredential 
    } from "@azure/identity";
    
    let credential;
    if (process.env.NODE_ENV === "production") {
        credential = new ManagedIdentityCredential();
    } else {
        credential = new AzureCliCredential();
    }
    
  • 无缝转换:应用可以在不更改身份验证代码的情况下从本地开发迁移到暂存或生产环境。

  • 改进的弹性:包括一种回退机制,当前一个凭据无法获取访问令牌时,该机制会切换到下一个凭据。

如何选择链接凭据

凭据链接有两种不同的方法:

DefaultAzureCredential 概述

DefaultAzureCredential 是一个固定的预配置凭据链。 它旨在支持许多环境,以及最常见的身份验证流和开发人员工具。 在图形形式中,基础链如下所示:

凭据链序列图,其中显示了在获取访问令牌之前,身份验证尝试通过多个凭据进行。

DefaultAzureCredential 尝试凭据的顺序如下。

下单(O) 凭据 描述 默认情况下是否启用?
1 环境 读取 环境变量的集合,以确定是否为应用配置了应用程序服务主体(应用程序用户)。 如果是这样,DefaultAzureCredential 使用这些值向 Azure 对应用进行身份验证。 此方法最常用于服务器环境,但也可以在本地开发时使用。 是的
2 工作负载标识 如果应用部署到启用了工作负荷标识的 Azure 主机,请对该帐户进行身份验证。 是的
3 托管标识 如果应用部署到启用了托管标识的 Azure 主机,请使用该托管标识向 Azure 验证应用。 是的
4 Visual Studio Code 如果已安装通过 Visual Studio Code 的 Azure 资源扩展@azure/identity-vscode 包 进行身份验证的开发人员,请对该帐户进行身份验证。 是的
5 Azure 命令行界面 (CLI) 如果开发人员使用 Azure CLI 的 az login 命令向 Azure 进行身份验证,请使用同一帐户向 Azure 验证应用。 是的
6 Azure PowerShell 如果开发人员使用 Azure PowerShell 的 Connect-AzAccount cmdlet 向 Azure 进行身份验证,请使用同一帐户向 Azure 验证应用。 是的
7 Azure 开发者命令行界面 (CLI) 如果开发人员使用 Azure 开发人员 CLI 的 azd auth login 命令向 Azure 进行身份验证,请使用该帐户进行身份验证。 是的
8 Broker 使用通过代理登录到 OS 的默认帐户进行身份验证。 要求安装 @azure/identity-broker 包 是的

最简单的形式是,可以使用无参数版本的 DefaultAzureCredential,如下所示:

import { DefaultAzureCredential } from "@azure/identity";
import { BlobServiceClient } from "@azure/storage-blob";

// Acquire a credential object
const credential = new DefaultAzureCredential();

const blobServiceClient = new BlobServiceClient(
    `https://${storageAccountName}.blob.core.windows.net`,
    credential
);

如何自定义 DefaultAzureCredential

以下部分介绍了管理链中应包含哪些凭据的策略。

排除凭据类型类别

若要排除所有 Developer toolDeployed service 凭据,请将环境变量 AZURE_TOKEN_CREDENTIALS 分别设置为 proddev。 使用值 prod 时,基础凭据链如下所示:

凭据链序列图,其中显示了在获取访问令牌之前,身份验证尝试通过多个凭据进行。

使用值 dev 时,链如下所示:

DefaultAzureCredential 链的关系图,其中AZURE_TOKEN_CREDENTIALS设置为“dev”,其中显示了用于身份验证的开发人员工具凭据。

若要确保已定义环境变量并将其设置为受支持的字符串,请将 requiredEnvVars 属性设置为 AZURE_TOKEN_CREDENTIALS

const credential = new DefaultAzureCredential({ 
    requiredEnvVars: [ "AZURE_TOKEN_CREDENTIALS" ]
});

使用特定凭据

若要排除除一个凭据之外的所有凭据,请将环境变量 AZURE_TOKEN_CREDENTIALS 设置为凭据名称。 例如,可以通过将 DefaultAzureCredential 设置为 AzureCliCredential 来减少 AZURE_TOKEN_CREDENTIALS 链到 AzureCliCredential。 字符串比较以不区分大小写的方式执行。 环境变量的有效字符串值包括:

  • AzureCliCredential
  • AzureDeveloperCliCredential
  • AzurePowerShellCredential
  • EnvironmentCredential
  • ManagedIdentityCredential
  • VisualStudioCodeCredential
  • WorkloadIdentityCredential

重要

环境变量 AZURE_TOKEN_CREDENTIALS 支持包版本 4.11.0 及更高版本中的各个凭据名称 @azure/identity

若要确保已定义环境变量并将其设置为受支持的字符串,请将 requiredEnvVars 属性设置为 AZURE_TOKEN_CREDENTIALS

const credential = new DefaultAzureCredential({ 
    requiredEnvVars: [ "AZURE_TOKEN_CREDENTIALS" ]
});

ChainedTokenCredential 概述

ChainedTokenCredential 是一个空链,可向其添加凭据以满足应用的需求。 例如:

import { 
    ChainedTokenCredential, 
    AzureCliCredential, 
    VisualStudioCodeCredential 
} from "@azure/identity";

const credential = new ChainedTokenCredential(
    new AzureCliCredential(),
    new VisualStudioCodeCredential()
);

const blobServiceClient = new BlobServiceClient(
    `https://${storageAccountName}.blob.core.windows.net`,
    credential
);

前面的代码示例创建由两个开发时凭据组成的定制凭据链。 首先尝试 AzureCliCredential,然后是 VisualStudioCodeCredential(如有必要)。 在图形形式中,链如下所示:

凭据链的示意图,其中 AzureCliCredential 作为首选尝试,VisualStudioCodeCredential 作为备用方案。

提示

为了提高性能,请优化 ChainedTokenCredential 中凭据的排序,使其按使用频率从高到低排列。

DefaultAzureCredential 的使用指南

DefaultAzureCredential 无疑是开始使用 Azure 标识库的最简单方法,但这种便利性也意味着需要权衡取舍。 将应用部署到 Azure 后,应了解应用的身份验证要求。 因此,请将 DefaultAzureCredential 替换为特定的 TokenCredential 实现,例如 ManagedIdentityCredential

原因如下:

  • 调试挑战:身份验证失败时,调试和识别违规凭据可能很困难。 必须启用日志记录,才能查看从一个凭据到下一个凭据的进度以及每个凭据的成功/失败状态。 有关详细信息,请参阅 调试凭据
  • 性能开销:按顺序尝试多个凭据的过程可能会导致性能开销。 例如,在本地开发计算机上运行时,托管标识不可用。 因此,ManagedIdentityCredential 在本地开发环境中始终失败。
  • 不可预知的行为DefaultAzureCredential检查是否存在某些环境变量。 有人可以在主机上的系统级别添加或修改这些环境变量。 这些更改在全局范围内适用,因此会在该计算机上运行的任何应用中改变 DefaultAzureCredential 在运行时的行为。

调试凭据

若要诊断意外问题或了解凭据的具体操作,请在应用中启用日志记录。 例如:

import { setLogLevel, AzureLogger } from "@azure/logger";
import { BlobServiceClient } from "@azure/storage-blob";
import { DefaultAzureCredential } from "@azure/identity";

// Constant for the Azure Identity log prefix
const AZURE_IDENTITY_LOG_PREFIX = "azure:identity";

// override logging to output to console.log (default location is stderr)
// only log messages that start with the Azure Identity log prefix
setLogLevel("verbose");
AzureLogger.log = (...args) => {
  const message = args[0];
  if (typeof message === 'string' && message.startsWith(AZURE_IDENTITY_LOG_PREFIX)) {
    console.log(...args);
  }
};

// Get storage account name from environment variable
const storageAccountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;

if (!storageAccountName) {
    throw new Error("AZURE_STORAGE_ACCOUNT_NAME environment variable is required");
}

const credential = new DefaultAzureCredential({ 
    requiredEnvVars: [ "AZURE_TOKEN_CREDENTIALS" ]
});


const blobServiceClient = new BlobServiceClient(
    `https://${storageAccountName}.blob.core.windows.net`,
    credential
);
azure:identity:info EnvironmentCredential => Found the following environment variables: 
azure:identity:verbose EnvironmentCredential => AZURE_CLIENT_SEND_CERTIFICATE_CHAIN: undefined; sendCertificateChain: false
azure:identity:info WorkloadIdentityCredential => Found the following environment variables:
azure:identity:warning DefaultAzureCredential => Skipped createDefaultWorkloadIdentityCredential because of an error creating the credential: CredentialUnavailableError: WorkloadIdentityCredential: is unavailable. clientId is a required parameter. In DefaultAzureCredential and ManagedIdentityCredential, this can be provided as an environment variable - "AZURE_CLIENT_ID".
        See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot
azure:identity:info ManagedIdentityCredential => Using DefaultToImds managed identity.
azure:identity:warning DefaultAzureCredential => Skipped createDefaultBrokerCredential because of an error creating the credential: Error: Broker for WAM was requested, but no plugin was configured or no authentication record was found. You must install the @azure/identity-broker plugin package (npm install --save @azure/identity-broker) and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling useIdentityPlugin(nativeBrokerPlugin) before using enableBroker.
azure:identity:info DefaultAzureCredential => getToken() => Skipping createDefaultWorkloadIdentityCredential, reason: WorkloadIdentityCredential: is unavailable. clientId is a required parameter. In DefaultAzureCredential and ManagedIdentityCredential, this can be provided as an environment variable - "AZURE_CLIENT_ID".
        See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot
azure:identity:info ManagedIdentityCredential => getToken() => Using the MSAL provider for Managed Identity.
azure:identity:info ManagedIdentityCredential - Token Exchange => ManagedIdentityCredential - Token Exchange: Unavailable. The environment variables needed are: AZURE_CLIENT_ID (or the client ID sent through the parameters), AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE
azure:identity:info ManagedIdentityCredential => getToken() => MSAL Identity source: DefaultToImds
azure:identity:info ManagedIdentityCredential => getToken() => Using the IMDS endpoint to probe for availability.
azure:identity:info ManagedIdentityCredential - IMDS => ManagedIdentityCredential - IMDS: Pinging the Azure IMDS endpoint
azure:identity:verbose ManagedIdentityCredential - IMDS => ManagedIdentityCredential - IMDS: Caught error RestError: connect ENETUNREACH 169.254.169.254:80
azure:identity:info ManagedIdentityCredential - IMDS => ManagedIdentityCredential - IMDS: The Azure IMDS endpoint is unavailable
azure:identity:error ManagedIdentityCredential => getToken() => ERROR. Scopes: https://storage.azure.com/.default. Error message: Attempted to use the IMDS endpoint, but it is not available..
azure:identity:info AzureCliCredential => getToken() => Using the scope https://storage.azure.com/.default
azure:identity:info AzureCliCredential => getToken() => expires_on is available and is valid, using it
azure:identity:info AzureCliCredential => getToken() => SUCCESS. Scopes: https://storage.azure.com/.default.

在前面的输出中,请注意 DefaultAzureCredential 已成功使用 AzureCliCredential 获取令牌。