Node.js使用DefaultAzureCredential认证Azure API Management报错如何解决
问题描述
我尝试在Node.js中使用@azure/identity包的DefaultAzureCredential完成Azure身份认证,以获取Azure API Management Service的报表数据,目前遇到认证及权限报错,具体操作和现象如下:
已完成操作
- 在Azure Portal创建了API Management Service实例
- 完成Azure AD应用注册并创建了服务主体
- 正确配置了使用
DefaultAzureCredential所需的环境变量:AZURE_TENANT_ID、AZURE_CLIENT_ID、AZURE_CLIENT_SECRET、AZURE_SUBSCRIPTION、AZURE_RESOURCE_GROUP、AZURE_SERVICE_NAME
现在遇到两个问题:
- 打印
new DefaultAzureCredential()的返回结果显示UnavailableMessage: 'DefaultAzureCredential => failed to retrieve a token from the included credentials'
相关代码:
require("dotenv").config(); const { DefaultAzureCredential } = require("@azure/identity"); const { ApiManagementClient } = require("@azure/arm-apimanagement"); if (!process.env.AZURE_TENANT_ID) throw Error("AZURE_TENANT_ID is missing from environment variables."); if (!process.env.AZURE_CLIENT_ID) throw Error("AZURE_CLIENT_ID is missing from environment variables."); if (!process.env.AZURE_CLIENT_SECRET) throw Error("AZURE_CLIENT_SECRET is missing from environment variables."); if (!process.env.AZURE_RESOURCE_GROUP) throw Error("AZURE_RESOURCE_GROUP is missing from environment variables."); if (!process.env.AZURE_SERVICE_NAME) throw Error("AZURE_SERVICE_NAME is missing from environment variables."); if (!process.env.AZURE_SUBSCRIPTION) throw Error("AZURE_SUBSCRIPTION is missing from environment variables."); const subscriptionId = process.env.AZURE_SUBSCRIPTION; const credentials = new DefaultAzureCredential(); console.log(credentials);
返回内容:
DefaultAzureCredential { UnavailableMessage: 'DefaultAzureCredential => failed to retrieve a token from the included credentials', _sources: [ EnvironmentCredential { _credential: [ClientSecretCredential] }, ManagedIdentityCredential { isEndpointUnavailable: null, clientId: 'c8xxxxxxxx5ac8', identityClient: [IdentityClient] }, AzureCliCredential {}, VisualStudioCodeCredential { cloudName: 'AzureCloud', identityClient: [IdentityClient], tenantId: 'common' } ] }
- 继续调用APIM报表接口返回403权限错误
相关代码:
const client = new ApiManagementClient(credentials, subscriptionId); const resourceGroupName = process.env.AZURE_RESOURCE_GROUP; const serviceName = process.env.AZURE_SERVICE_NAME; const filter = "callCountSuccess"; client.reports .listBySubscription( resourceGroupName, serviceName, filter ) .then((result) => { console.log(JSON.stringify(result)); }) .catch((err) => { console.log(err); });
错误返回:
response: { body: `{"error": {"code":"AuthorizationFailed", "message": "The client 'cxxxxxxxxxxxxxxx569' with object id 'cxxxxxxxxxxxxxxx569' does not have authorization to perform action 'Microsoft.ApiManagement/service/reports/read' over scope '/subscriptions/85xxxxxxx3c5/resourceGroups/axxxb/providers/Microsoft.ApiManagement/service/Axxxx/reports/bySubscription' or the scope is invalid. If access was recently granted, please refresh your credentials."}}`, headers: HttpHeaders { _headersMap: [Object] }, status: 403 },
补充说明
已经为服务主体授予了API Management Service的Reader角色,仍然返回上述403错误,角色配置截图如下:
排查解决步骤
1. 处理DefaultAzureCredential的提示问题
你看到的不可用消息是初始化阶段的预检测提示,不代表实际调用时无法获取凭证,不需要纠结这个输出,只要后续接口能正常拿到token就属于正常情况。如果要完全消除这个提示,可以直接显式使用ClientSecretCredential代替DefaultAzureCredential,规避其他无关凭证源的检测逻辑,代码修改如下:
// 替换原有DefaultAzureCredential引入 const { ClientSecretCredential } = require("@azure/identity"); // 初始化凭证 const credentials = new ClientSecretCredential( process.env.AZURE_TENANT_ID, process.env.AZURE_CLIENT_ID, process.env.AZURE_CLIENT_SECRET );
2. 解决403权限问题
通用Reader角色默认没有APIM报表的读取权限,按以下步骤排查修改即可:
- 确认角色分配范围:确认给服务主体分配角色的范围和403错误里提示的scope路径匹配,错误里的scope是单个APIM实例级别,要确保角色是分配在当前调用的APIM实例上,而不是其他层级。
- 替换对应角色:通用Reader角色不包含
Microsoft.ApiManagement/service/reports/read权限,给服务主体分配「API管理服务读者」内置角色即可,该角色专门包含APIM相关的所有读取权限,包括报表读取权限。 - 等待权限生效:新分配的Azure角色最长需要15分钟才能完全生效,分配后可以等待几分钟再测试,也可以重新生成服务主体的client secret,更新环境变量后再测试。
- 修正filter参数格式:当前的filter参数不符合APIM报表接口的OData格式要求,正确格式比如要筛选成功调用数相关数据,应该写成
$filter=callCountSuccess ge 0,参数格式错误也可能触发权限类错误提示。
内容的提问来源于stack exchange,提问作者Ammar Abdul Aziz
相关产品推荐
相关产品推荐

