1
votes

I'm trying to authenticate to an Azure Key Vault from an App Service (a Web API) using the system-assigned identity of the App Service. In the Azure Key Vault, I have created an an access policy that gives the App Service access to Keys, Secrets, and Certificates (will limit this later on!).

In the App Service, I try to obtain an authentication token (see code snippet), but GetAccessTokenAsync gives me an exception from Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider with the message show below.

Searching, I have found quite a few descriptions of this error, but no useful hints about the cause or a solution.

Please note that I have no problem authenticating to that same key vault using a service principal, but the idea of using a managed identity is, of course, to avoid storing credentials like client ID and secret anywhere

        var azureServiceTokenProvider = new AzureServiceTokenProvider();
        string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(VaultUrl);

        Client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

Exception message:

Parameters: Connection String: [No connection string specified], Resource: https://efipsexternals.vault.azure.net/, Authority: . Exception Message: Tried the following 4 methods to get an access token, but none of them worked.\r\nParameters: Connection String: [No connection string specified], Resource: https://<VAULTNAME>.vault.azure.net/, Authority: . Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. Received a non-retryable error. MSI ResponseCode: BadRequest, Response: {\"ExceptionMessage\":\"AADSTS500011: The resource principal named https://<VAULTNAME>.vault.azure.net/ was not found in the tenant named <TENANTID>. 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 might have sent your authentication request to the wrong tenant.\\r\\nTrace ID: 52674a60-5e9c-432c-b999-e13e326f2000\\r\\nCorrelation ID: b335c7b2-2f38-4cc0-9ec3-6662c7ee5546\\r\\nTimestamp: 2019-10-27 10:58:52Z\",\"ErrorCode\":\"invalid_resource\",\"ServiceErrorCodes\":[\"500011\"],\"StatusCode\":400,\"Message\":null,\"CorrelationId\":\"102c92f1-525a-44ee-a383-d7e40ccd2ed4\"}\r\nParameters: Connection String: [No connection string specified], Resource: https://<VAULTNAME>.vault.azure.net/, Authority: . Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at \"D:\\local\\LocalAppData\\.IdentityService\\AzureServiceAuth\\tokenprovider.json\"\r\nParameters: Connection String: [No connection string specified], Resource: https://<VAULTNAME>.vault.azure.net/, Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. 'az' is not recognized as an internal or external command,\r\noperable program or batch file.\r\n\r\nParameters: Connection String: [No connection string specified], Resource: https://<VAULTNAME>.vault.azure.net/, Authority: https://login.microsoftonline.com/common. Exception Message: Tried to get token using Active Directory Integrated Authentication. Access token could not be acquired. Failed to get user name from the operating system.Inner Exception : The format of the specified domain name is invalid\r\n
2
Based on my test, you can just remove string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(VaultUrl); is - Jack Jia
@JackJia, you are absolutely right. May I suggest that you please repeat this in an answer so that I can accept it? - bmoss
Thanks. Added my answer. - Jack Jia

2 Answers

1
votes

To acquire an access token with managed identity for azure key vault, you just need to:

var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

The code string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(VaultUrl); is not necessary here, and it should be the root cause of your issue. You might pass your keyvault url as the parameter to it which is not appropriate here. The right one for accessing keyvault API is : https://vault.azure.com/. If you want to use this piece of code, you can do as following:

public static async Task<string> AuthenticationCallback(string authority, string resource, string scope)
{
    Console.WriteLine($"authority:{authority}, resource:{resource}, scope:{scope}");
    return new AzureServiceTokenProvider().GetAccessTokenAsync(resource).Result;
}

static void Main(string[] args)
{
    Console.ReadLine();

    string baseUrl = "your keyvault url, for example: https://jackkv.vault.azure.net/";
    KeyVaultClient kvc = new KeyVaultClient(AuthenticationCallback);
    SecretBundle secret = kvc.GetSecretAsync(baseUrl, "testSecret").Result;
    Console.WriteLine(secret.Value);

    Console.ReadLine();
}

0
votes

The GetAccessTokenAsync method requires a resource identifier like here https://vault.azure.com/.

// Instantiate a new KeyVaultClient object, with an access token to Key Vault
var azureServiceTokenProvider1 = new AzureServiceTokenProvider();
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider1.KeyVaultTokenCallback));

// Optional: Request an access token to Key Vault
var azureServiceTokenProvider2 = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider2.GetAccessTokenAsync("https://vault.azure.com/").ConfigureAwait(false);

When you get Connection String: [No connection string specified] error, try the below way to troubleshoot.

1.Pass RunAs=App; in the connectionString parameter of AzureServiceTokenProvider. This way it will not try different modes to obtain a token, and the exception is a bit better.

2.Install/update the latest version of Microsoft.Azure.Services.AppAuthentication.

For more details, you could refer to this article.