問題描述
使用Azure密鑰保管庫(Key Vault)來托管存儲賬號(Storage Account)密鑰的示例中,從Github中下載的示例代碼在中國區Azure運行時候會遇見各種認證和授權問題,以下列舉出運行代碼中遇見的各種異常:
- "AADSTS90002: Tenant 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.
- Microsoft.Rest.Azure.CloudException | HResult=0x80131500 | Message=The subscription 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' could not be found. | Source=Microsoft.Azure.Management.KeyVault
-
The client 'xxxxxxxx-e256-xxxx-8ef8-xxxxxxxxxxxx' with object id 'xxxxxxxx-e256-xxxx-xxxxxxxxxxxx' does not have authorization to perform action 'Microsoft.KeyVault/vaults/read' over scope '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/dev-service-rg/providers/Microsoft.KeyVault/vaults/<youkeyvaultname>' or the scope is invalid. If access was recently granted, please refresh your credentials.
-
Unexpected exception encountered: AADSTS700016: Application with identifier '54d5b1e9-5f5c-48f1-8483-d72471cbe7e7' was not found in the directory 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
- {"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 57169df7-d54d-4533-b6cf-fc269ee93f00\r\nCorrelation ID: 33fb61c4-7266-4690-bb8d-4d4ebb5614f5\r\nTimestamp: 2021-01-19 02:44:50Z"}
-
AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token. |Trace ID: cbfb3d00-a3e5-445e-96b3-918a94054100 |Correlation ID: 40964a5f-e267-43da-988a-00bf33fa7ad4 |Timestamp: 2021-01-19 03:16:38Z
以上錯誤就是在調試Key vault dotnet managed storage代碼的過程(https://github.com/Azure-Samples/key-vault-dotnet-managed-storage)中遇見的錯誤。下面我們一一的解決以上錯誤並使得程序成功運行:
調試代碼
首先通過Github下載代碼並在Azure環境中准備好AAD,Key Vault,Storage Account。
- git clone https://github.com/Azure-Samples/key-vault-dotnet-managed-storage.git
- 用VS 2019打開后,編輯app.config文件, 配置tenant, subscription, AD app id and secret, and storage account and its resource id等值
PS: 獲取AAD中注冊應用的相應配置值,可以參考博文:
【Azure Developer】使用Postman獲取Azure AD中注冊應用程序的授權Token,及為Azure REST API設置Authorization
【Azure Developer】Python代碼通過AAD認證訪問微軟Azure密鑰保管庫(Azure Key Vault)中機密信息(Secret)
第一個錯誤:"AADSTS90002: Tenant 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.
這是因為代碼默認是連接到Global Azure的AAD環境,所以認證的時候會把app.config中的tenant值在Global Azure AAD中查找。而我們在項目中配置的Tenant是中國區Azure的,所以出現not found的提示。 只需要在代碼中指定AAD的環境中Azure China即可解決該問題。
- 在ClientContext.cs文件中 修改GetServiceCredentialsAsync方法ActiveDirectoryServiceSettings.Azure 為ActiveDirectoryServiceSettings.AzureChina
/// <summary> /// Returns a task representing the attempt to log in to Azure public as the specified /// service principal, with the specified credential. /// </summary> /// <param name="certificateThumbprint"></param> /// <returns></returns> public static Task<ServiceClientCredentials> GetServiceCredentialsAsync(string tenantId, string applicationId, string appSecret) { if (_servicePrincipalCredential == null) { _servicePrincipalCredential = new ClientCredential(applicationId, appSecret); } //Update the Azure to Azure China return ApplicationTokenProvider.LoginSilentAsync( tenantId, _servicePrincipalCredential, ActiveDirectoryServiceSettings.AzureChina, TokenCache.DefaultShared); }
第二個錯誤:Microsoft.Rest.Azure.CloudException | HResult=0x80131500 | Message=The subscription 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' could not be found. | Source=Microsoft.Azure.Management.KeyVault
這個錯誤的原因為在Azure Management KeyVault對象中找不到我們在項目中配置的訂閱信息。在Debug代碼時候才發現,KeyVaultManagementClient對象默認的URL也是指向Global Azure。中國區的Key Vault Management的URL為https://management.chinacloudapi.cn, 與Global不同。需要在KeyVaultSampleBase.cs代碼中設定ManagementClient.BaseUri = new Uri("https://management.chinacloudapi.cn"); 即可。
private void InstantiateSample(string tenantId, string appId, string appSecret, string subscriptionId, string resourceGroupName, string vaultLocation, string vaultName, string storageAccountName, string storageAccountResourceId) { context = ClientContext.Build(tenantId, appId, appSecret, subscriptionId, resourceGroupName, vaultLocation, vaultName, storageAccountName, storageAccountResourceId); // log in with as the specified service principal for vault management operations var serviceCredentials = Task.Run(() => ClientContext.GetServiceCredentialsAsync(tenantId, appId, appSecret)).ConfigureAwait(true).GetAwaiter().GetResult(); // instantiate the management client ManagementClient = new KeyVaultManagementClient(serviceCredentials); ManagementClient.BaseUri = new Uri("https://management.chinacloudapi.cn"); ManagementClient.SubscriptionId = subscriptionId; // instantiate the data client, specifying the user-based access token retrieval callback DataClient = new KeyVaultClient(ClientContext.AcquireUserAccessTokenAsync); }
第三個錯誤:The client 'xxxxxxxx-e256-xxxx-8ef8-xxxxxxxxxxxx' with object id 'xxxxxxxx-e256-xxxx-xxxxxxxxxxxx' does not have authorization to perform action 'Microsoft.KeyVault/vaults/read' over scope '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/dev-service-rg/providers/Microsoft.KeyVault/vaults/<youkeyvaultname>' or the scope is invalid. If access was recently granted, please refresh your credentials.
在通過代碼獲取Key Vault Management對象時候,由於程序當前使用的AAD注冊應用沒有被授予Key Vault的操作權限,所以出現does not have authorization to perform action 'Microsoft.KeyVault/vaults/read' 。通過到Key Vault的門戶中為AAD應用分配權限即可解決此問題。
- Key Vault Portal -> Access Control(IAM) -> Add Role Assignment.
第四個錯誤:Unexpected exception encountered: AADSTS700016: Application with identifier '54d5b1e9-5f5c-48f1-8483-d72471cbe7e7' was not found in the directory 'xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
這個錯誤很迷惑,因為identifier “54d5b1e9-5f5c-48f1-8483-d72471cbe7e7”並不包含在配置中,它是如何產生的呢? 在全局查看項目文件后,發現它是代碼中hardcode的一個值。需要在使用時候把替換為app.config中的application id。
- 在SampleConstants.cs文件中的WellKnownClientId
public static string WellKnownClientId { // Native AD app id with permissions in the subscription // Consider fetching it from configuration. get { return "54d5b1e9-5f5c-48f1-8483-d72471cbe7e7"; } }
- 在ClientContext.cs文件中使用ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.VaultMgmtAppId]替換WellKnownClientId
public static async Task<string> AcquireUserAccessTokenAsync(string authority, string resource, string scope) { var context = new AuthenticationContext(authority, TokenCache.DefaultShared); if (_deviceCodeResponse == null) { //_deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, SampleConstants.WellKnownClientId).ConfigureAwait(false); _deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.VaultMgmtAppId]).ConfigureAwait(false); Console.WriteLine("############################################################################################"); Console.WriteLine("To continue with the test run, please follow these instructions: {0}", _deviceCodeResponse.Message); Console.WriteLine("############################################################################################"); } //context.AcquireTokenAsync() var result = await context.AcquireTokenByDeviceCodeAsync(_deviceCodeResponse).ConfigureAwait(false); return result.AccessToken; }
第五個錯誤:{"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 57169df7-d54d-4533-b6cf-fc269ee93f00\r\nCorrelation ID: 33fb61c4-7266-4690-bb8d-4d4ebb5614f5\r\nTimestamp: 2021-01-19 02:44:50Z"}
這個錯誤是在 context.AcquireTokenByDeviceCodeAsync時,由於配置的AAD應用中沒有開啟移動應用或客戶端應用的高級設置。詳細的分析可以參考博客:https://blogs.aaddevsup.xyz/2019/08/receiving-error-aadsts7000218-the-request-body-must-contain-the-following-parameter-client_assertion-or-client_secret/
第六個錯誤:AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token. |Trace ID: cbfb3d00-a3e5-445e-96b3-918a94054100 |Correlation ID: 40964a5f-e267-43da-988a-00bf33fa7ad4 |Timestamp: 2021-01-19 03:16:38Z
這個錯誤發生在 retrievedMsaResponse = await sample.DataClient.GetStorageAccountWithHttpMessagesAsync(vaultUri, managedStorageName).ConfigureAwait(false)的部分,由於通過AcquireTokenByDeviceCodeAsync獲取到的token只能完成一次認證。所以再一次調用KeyVaultClient的Get-xxxxx-WithHttpMessageAsync時就會出現OAuth2 Authorization code was already redeemed錯誤。 解決方法為修改創建 DataClient = new KeyVaultClient(ClientContext.AcquireUserAccessTokenAsync)的認證方式或者是AcquireUserAccessTokenAsync中的獲取token方式。
private void InstantiateSample(string tenantId, string appId, string appSecret, string subscriptionId, string resourceGroupName, string vaultLocation, string vaultName, string storageAccountName, string storageAccountResourceId) { context = ClientContext.Build(tenantId, appId, appSecret, subscriptionId, resourceGroupName, vaultLocation, vaultName, storageAccountName, storageAccountResourceId); // log in with as the specified service principal for vault management operations var serviceCredentials = Task.Run(() => ClientContext.GetServiceCredentialsAsync(tenantId, appId, appSecret)).ConfigureAwait(true).GetAwaiter().GetResult(); //var serviceCredentials =ClientContext.GetServiceCredentialsAsync(tenantId, appId, appSecret).Result; // instantiate the management client ManagementClient = new KeyVaultManagementClient(serviceCredentials); ManagementClient.BaseUri = new Uri("https://management.chinacloudapi.cn"); ManagementClient.SubscriptionId = subscriptionId; // instantiate the data client, specifying the user-based access token retrieval callback DataClient = new KeyVaultClient(ClientContext.AcquireUserAccessTokenAsync);
//DataClient = new KeyVaultClient(serviceCredentials); }
或者
public static async Task<string> AcquireUserAccessTokenAsync(string authority, string resource, string scope) { var context = new AuthenticationContext(authority, TokenCache.DefaultShared); if (_deviceCodeResponse == null) { //_deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, SampleConstants.WellKnownClientId).ConfigureAwait(false); _deviceCodeResponse = await context.AcquireDeviceCodeAsync(resource, ConfigurationManager.AppSettings[SampleConstants.ConfigKeys.VaultMgmtAppId]).ConfigureAwait(false); Console.WriteLine("############################################################################################"); Console.WriteLine("To continue with the test run, please follow these instructions: {0}", _deviceCodeResponse.Message); Console.WriteLine("############################################################################################"); } //context.AcquireTokenAsync() var result = await context.AcquireTokenByDeviceCodeAsync(_deviceCodeResponse).ConfigureAwait(false);
return result.AccessToken; }
(PS: 以上第六個錯誤還沒有完全解決。)
參考資料:
Azure Sample:https://github.com/Azure-Samples
RECEIVING ERROR AADSTS7000218: THE REQUEST BODY MUST CONTAIN THE FOLLOWING PARAMETER: ‘CLIENT_ASSERTION’ OR ‘CLIENT_SECRET’ :https://blogs.aaddevsup.xyz/2019/08/receiving-error-aadsts7000218-the-request-body-must-contain-the-following-parameter-client_assertion-or-client_secret/
Python代碼通過AAD認證訪問微軟Azure密鑰保管庫(Azure Key Vault)中機密信息(Secret): https://www.cnblogs.com/lulight/p/14286396.html