使用托管标识在 Azure 中进行应用服务到应用服务的身份验证

use*_*915 6 azure azure-active-directory azure-web-app-service

我在 Azure 中设置了两个应用服务。'Parent' 和 'Child' 都公开 API 端点。

  • 子节点具有端点“Get”。
  • Parent 具有端点“Get”和“GetChild”(使用 HttpClient 在 Child 上调用“Get”)。

我希望所有子端点都需要通过托管身份和 AAD 进行身份验证,并且我希望所有父端点都允许匿名。但是在 Azure 中,我想将父应用服务设置为有权调用子应用服务。因此,子端点只能通过使用父端点访问(或者如果您对用户帐户具有直接使用子端点的权限)。

在 Azure 门户中:

认证/授权

  • 我在两个应用服务上都启用了“应用服务身份验证”。
  • 孩子设置为“使用 AAD 登录”。
  • 父级设置为“允许匿名请求”。
  • 两者都在“身份验证提供程序”下配置了 AAD。

身份

  • 将两个应用服务都设置为“开启”

访问控制 (IAM)

  • 子级将父级作为角色分配,类型 =“应用服务或功能应用”,角色 =“贡献者”

With all the above setup:

  • Calling Child -> Get, requires me to log in
  • Calling Parent -> Get, returns the expected response of 200 OK
  • Calling Parent -> GetChild, returns "401 - You do not have permission to view this directory or page"

Without the use of Client ids/Secrets/Keys/etc, as I thought the idea behind Managed Identity was to throw that all out the window, given all the above, should Parent be able to call Child? And if so, what have I setup wrong?

Roh*_*gal 8

  • 调用 Parent -> GetChild,返回“401 - 您无权查看此目录或页面”

如果不使用客户端 ids/Secrets/Keys/etc,因为我认为托管身份背后的想法是将所有这些都扔到窗外,考虑到上述所有内容,父级是否应该能够调用子级?如果是这样,我设置错了什么?

我注意到当前设置有两件事。

1. 使用托管身份获取令牌,以从“父级”调用“子级”服务端点

托管身份仅向您的应用程序服务提供身份(无需管理/维护应用程序机密或密钥的麻烦)。然后可以使用此身份来获取不同 Azure 资源的令牌。

但您的应用程序仍然有责任利用此身份并获取相关资源的令牌。在这种情况下,相关资源将是您的“子”API。我想这可能是你现在缺少的部分。

Microsoft Docs 上的相关文档 -如何使用应用服务和 Azure Functions 的托管标识 > 获取 Azure 资源的令牌

using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
// ...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net");

// change this to use identifierUri for your child app service. 
// I have used the default value but in case you've used a different value, find it by going to Azure AD applications > your app registration > manifest
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://<yourchildappservice>.azurewebsites.net");
Run Code Online (Sandbox Code Playgroud)

此 C#/.NET 示例使用Microsoft.Azure.Services.AppAuthenticationnuget 包并获取 Azure Key Vault 的令牌。在您的情况下,您将替换https://vault.azure.net为“子”服务的identifierUri。它通常设置https://<yourappservicename>.azurewebsites.net为默认值,但您可以通过转到 Azure AD 应用程序,然后查找相关的应用程序注册 > 清单来找到它的值。您还可以使用目标应用程序(即“子”)的 applicationId 来获取令牌。

如果您不使用 C#/.NET,上面的同一 Microsoft 文档链接还提供了有关如何使用托管身份和来自任何平台的基于 REST 的调用来获取令牌的指南。使用 REST 协议

这是一篇博客文章,也提供了很好的演练 -使用托管服务身份 (MSI) 调用 Azure AD 受保护的网站

2. Azure RBAC 角色分配与您可能想要使用的 Azure AD 角色不同

我看到您已从 IAM 为父应用服务的身份分配了贡献者角色。此角色分配适用于 Azure RBAC,并有助于授予管理资源的权限,但 Azure AD 角色声明的工作方式有所不同。

如果您想要做的是将角色分配给父应用程序,可以在子应用程序中检查该角色,然后才允许调用,则有一种不同的设置方法。

我首先应该提到的是,这种基于角色的设置适用于一些高级场景,并不是真正强制执行的。按照上述第 1 点中的步骤操作后,您应该能够从“家长”调用“孩子”服务。

现在,一旦从父级到子级的调用正常工作,您可能希望将对子级应用程序服务的访问限制为仅“父级”或几个有效的应用程序。以下是实现这一目标的两种方法。

这两种方法均在 Microsoft Docs 中进行了说明 - Microsoft 身份平台和 OAuth 2.0 客户端凭据流程

关联 SO 帖子和博客

方法 1 - 使用访问控制列表

当您的“子”API 收到令牌时,它可以解码该令牌并从appidiss声明中提取客户端的应用程序 ID。然后,它将应用程序与其维护的访问控制列表 (ACL) 进行比较。

根据您的要求,API 可能仅向特定客户端授予完整权限的子集或所有权限。

方法 2 - 使用应用程序权限或角色

配置您的子 API 应用程序以公开一组应用程序权限(或角色)。

这种方法更具声明性,因为您定义了一个应用程序权限,需要将其分配给任何可以调用您的child-api.

导航到 Azure Active Directory > 应用程序注册 > 应用程序的应用程序注册child-api> 清单

添加一个新的应用程序角色..使用 json,如下所示:

"appRoles": [
{
  "allowedMemberTypes": [
    "Application"
  ],
  "displayName": "Can invoke my API",
  "id": "fc803414-3c61-4ebc-a5e5-cd1675c14bbb",
  "isEnabled": true,
  "description": "Apps that have this role have the ability to invoke my child API",
  "value": "MyAPIValidClient"
}]
Run Code Online (Sandbox Code Playgroud)

将应用程序权限分配给您的前端应用程序

New-AzureADServiceAppRoleAssignment -ObjectId <parentApp.ObjectId> -PrincipalId <parentApp.ObjectId> -Id "fc803414-3c61-4ebc-a5e5-cd1675c14bbb" -ResourceId <childApp.ObjectId>
Run Code Online (Sandbox Code Playgroud)

现在,在子 api 收到的身份验证令牌中,您可以检查角色声明集合是否必须包含名为“MyAPIValidClient”的角色,否则您可以拒绝调用并出现未经授权的异常。