如何使用复合标识向 Azure 进行身份验证以访问 Azure Key Vault

How to use compound identity to authenticate with Azure to access Azure Key Vault

问题:

A​​zure Key Vault 支持“复合身份”来控制访问(https://docs.microsoft.com/en-us/azure/key-vault/general/secure-your-key-vault),但是有没有人对如何使用复合身份在 .NET 中执行身份验证有一些经验?

当 运行 从桌面使用本机应用程序时,我相信身份验证将涉及:

  1. 用户的交互式登录和同意,以及
  2. 应用程序使用客户端密钥进行身份验证

身份验证工作流程是什么样的?有可用的例子吗?我们可以使用 MSAL 实现吗?

关于复合标识的一些解释:

假设我们创建了一个 Azure Key Vault 并在该保管库中保存了一些秘密。如何在 Windows 10:

下的桌面应用程序 运行 中实现以下功能

换句话说,我希望可以通过两种身份验证的组合访问密钥库资源

实际上,如果您授予访问权限,例如secrets,它不允许用户在 Azure 门户中查看 Key Vault。 如果他们对 Azure RBAC 中的资源没有读取权限,则无法查看它。 因此,您应该能够为用户添加访问权限并代表用户直接从应用程序调用它。

另一种方法是使用 back-end 检查用户 ID 并访问 Key Vault 而不是用户。

  • 用户登录应用程序
  • 应用为用户
  • 获取back-end的访问令牌
  • 应用使用令牌
  • 调用back-end
  • Back-end 验证令牌,检查是否允许此用户访问
  • 使用仅使用 back-end 客户端凭据获取的访问令牌从 Key Vault 获取机密

这里你只需要允许 back-end 应用程序访问 Key Vault 本身。

我要回答我的问题了。简短的回答是使用

IConfidentialClientApplication.AcquireTokenOnBehalfOf(
      IEnumerable<string> scopes,
      UserAssertion userAssertion);

交互式获取的用户令牌可以用作UserAssertion。

长版,因为我是新手,所以我将详细介绍我发现的所有细节。事实证明这里和那里有一些位可以创建一个完整的可运行的 .net 应用程序,所以并不是所有的事情都与我的问题直接相关。

1。在 Azure AD 中创建应用程序注册。

  1. 平台:移动和桌面应用程序

  2. 设置证书或秘密:我们将在此演示中使用秘密。

  3. Redirect URI:在Mobile and desktop application部分添加一个新的,并将其设置为http://127.0.0.1

    如果运行作为控制台应用程序,没有window直接关联到运行应用程序,所以执行用户log-in的最简单方法是使用系统默认网络浏览器应用程序。因此,使用返回代码的唯一方法是根据“http://localhost”或“http://127.0.0.1”使用 redir URL,又名环回 URL。在 run-time 中,MSAL 将使用动态端口作为本地网络服务器来捕获来自网络浏览器的 redir URL 调用。由于它是 运行 在本地,http:// 或 https:// 都是允许的,除非有人使用 DNS 或主机文件劫持了“localhost”。

  4. Set-up 一个 API 在“公开一个 API” 部分,并添加了一个范围。

    在 On-Behalf-Of 工作流中,用户 sign-in 使用应用程序提供的范围,而不是直接访问密钥保管库资源。我们需要“设置”应用程序 ID URI,并创建至少一个供交互式 sign-in.

    使用的范围

2。创建密钥库,并在数据平面中设置访问策略。

  1. 创建密钥保管库。

  2. 在“访问策略”中,添加新的访问策略。

    要创建复合身份,select Seelct principal 的有效用户或组帐户,以及 select 我们在上一步中为 Authorized application 创建的相同应用程序。

3。写代码

创建 .NET 核心控制台应用程序。添加以下 nuget 包

<PackageReference Include="Microsoft.Identity.Client" Version="4.18.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.8" />
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.5" />

这是使用复合身份访问密钥库的代码


const string AppClientId = "[Enter_your_Application_(client)_ID";
const string AppClientSecret = "[Enter_your_Application_(secret)";
const string TenantId = "[Enter_your_tenantId]";
const string KeyVaultBaseUri = "https://[your_keyvault_name].vault.azure.net/";

// In on-behalf-of flow, the following scope needs to be consented when acquiring the user token. Otherwise, the app cannot access the key vault on-behalf-of user.
const string KeyVaultUserImScope = "https://vault.azure.net/user_impersonation";
// In on-behalf-of flow, the following scope is used to access key vault data when acquiring client token
const string KeyVaultScope = "https://vault.azure.net/.default";
// An "Exposed API" in app registration is required when using on-behalf-of flow. 
const string AppClientScope = "[Enter_your_Application_ID_URI]/[Enter_Your_Scope_Name]";
const string Instance = "https://login.microsoftonline.com/";

Console.WriteLine("Acquire User token");
var pubClient = PublicClientApplicationBuilder.Create(AppClientId)
                .WithAuthority($"{Instance}{TenantId}")
                .WithRedirectUri("http://localhost")    // Make sure the "http://localhost" is added and selected as the app Redirect URI
                .Build();
var userResult= pubClient
                .AcquireTokenInteractive(new[] {AppClientScope })
                .WithExtraScopesToConsent(new [] {KeyVaultUserImScope})
                .WithPrompt(Prompt.Consent)
                .ExecuteAsync().Result;

// In normal case, when user token is directly given from outside, we should validate if the user Result has consented to the required customized scope AppClientScope before proceeded with next steps. Here we will ignore this step.


Console.WriteLine("Acquire Client token");
// The following two steps are equivalent to https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#middle-tier-access-token-request
var conClient = ConfidentialClientApplicationBuilder.Create(AppClientId)
                .WithAuthority($"{Instance}{TenantId}")
                .WithClientSecret(AppClientSecret)
                .Build();

            
var OboResult= conClient.AcquireTokenOnBehalfOf(
                    new[] {KeyVaultScope},
                    new UserAssertion(userReult.AccessToken))
                .ExecuteAsync().Result;



Console.WriteLine("Access Key Vault");
var kc = new KeyVaultCredential((authority, resource, scope) =>
                {
                    Console.WriteLine($"Authority: {authority}, Resource: {resource}, Scope: {scope}");
                    return Task.FromResult(OboResult.AccessToken);
                });

var kvClient = new KeyVaultClient(kc);
var secretBundle = await kvClient.GetSecretAsync(KeyVaultBaseUri, SecretName);

Console.WriteLine("Secret:" + secretBundle.Value);

如果我们不使用复合身份,我们可以使用Azure.Security.KeyVault.Secrets.SecretClient通过以下方法之一访问密钥保管库数据

// For access policy assigned to confidential application 
var client = new SecretClient(new Uri(KeyVaultBaseUri),
                new ClientSecretCredential(TenantId, AppClientId, AppClientSecret));
var secretBundle = await client.GetSecretAsync(SecretName);
Console.WriteLine("Secret:" + secretBundle.Value.Value);

// For access policy assigned to User or Group account
var client = new SecretClient(new Uri(KeyVaultBaseUri), new InteractiveBrowserCredential());
var secretBundle = await client.GetSecretAsync(SecretName);
Console.WriteLine("Secret:" + secretBundle.Value.Value);