Azure / azure-libraries-for-net

Azure libraries for .Net
MIT License
378 stars 193 forks source link

[BUG] Cannot communicate with server. No authentication token available for 'https://vault.azure.net/'. #1072

Open Rookian opened 4 years ago

Rookian commented 4 years ago

I created an Azure Key Vault and defined Access Policies for a group of which I am a member of. In the Azure Portal I can see that the Access Policies are defined correctly (All Access to Keys, Secrets and Certificates). The whole authentication is using AzureServiceTokenProviderwith RunAs=Developer; DeveloperTool=AzureCli (I also tried it with a Service Principal).

Code to connect to Azure:

public class AzureConnector
{
    public static async Task<IAzure> Connect(string tenantId, string subscriptionId, string clientId = null, string clien
    {
        var tokenProvider = GetAzureServiceTokenProvider(tenantId, clientId, clientSecret);

        var managementToken = await tokenProvider.GetAccessTokenAsync("https://management.azure.com/");
        var graphToken = await tokenProvider.GetAccessTokenAsync("https://graph.windows.net/");

        var azureCredentials = new AzureCredentials(
            new TokenCredentials(managementToken),
            new TokenCredentials(graphToken),
            tenantId,
            AzureEnvironment.AzureGlobalCloud);

        var azure = Azure
            .Configure()
            .WithLogLevel(HttpLoggingDelegatingHandler.Level.BodyAndHeaders)
            .Authenticate(azureCredentials)
            .WithSubscription(subscriptionId);

        return azure;
    }

    private static AzureServiceTokenProvider GetAzureServiceTokenProvider(string tenantId, string clientId,
        string clientSecret)
    {
        string connectionString;
        if (!string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(clientSecret))
        {
            connectionString = $"RunAs=App;AppId={clientId};TenantId={tenantId};AppKey={clientSecret}";
            Console.WriteLine($"using Service Principal '{clientId}'");
        }
        else
        {
            connectionString = "RunAs=Developer; DeveloperTool=AzureCli";
            Console.WriteLine("using Azure Cli");
        }

        var azureServiceTokenProvider = new AzureServiceTokenProvider(connectionString);
        return azureServiceTokenProvider;
    }
}

After I created Azure Key Vault successfully, I wanted to add a Key.

await vault.Keys.Define("XYZ")
    .WithKeyTypeToCreate(JsonWebKeyType.Rsa)
    .WithKeyOperations(JsonWebKeyOperation.Decrypt, JsonWebKeyOperation.Encrypt)
    .CreateAsync(cancellationToken);

This fails with

Microsoft.Rest.RestException: Cannot communicate with server. No authentication token available for 'https://vault.azure.net/'

I tried to create a separate instance of KeyVaultManager, but I am not allowed to use https://vault.azure.net/ as a resource.

var azureServiceTokenProvider = new AzureServiceTokenProvider("RunAs=Developer; DeveloperTool=AzureCli");
var token = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net/", cancellationToken: cancellationToken);

var keyVaultManager = KeyVaultManager.Authenticate(
    new AzureCredentials(new TokenCredentials(token), new TokenCredentials(token), vault.TenantId, AzureEnvironment.AzureGlobalCloud), api.SubscriptionId);

var byId = (await keyVaultManager.Vaults.ListByResourceGroupAsync(resourceGroup)).SingleOrDefault();

I have been able to create a Key by hand using the Azure Portal and by using the Azure CLI directly without any problems.

az keyvault key create --name xyz123 --vault-name $keyVaultName -o none

Exception or Stack Trace

Microsoft.Rest.RestException: Cannot communicate with server. No authentication token available for 'https://vault.azure.net/'. at Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Azure.KeyVault.KeyVaultClient.CreateKeyWithHttpMessagesAsync(String vaultBaseUrl, String keyName, String kty, Nullable1 keySize, IList1 keyOps, KeyAttributes keyAttributes, IDictionary2 tags, String curve, Dictionary2 customHeaders, CancellationToken cancellationToken) at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.CreateKeyAsync(IKeyVaultClient operations, String vaultBaseUrl, String keyName, String kty, Nullable1 keySize, IList1 keyOps, KeyAttributes keyAttributes, IDictionary2 tags, String curve, CancellationToken cancellationToken) at Microsoft.Azure.Management.KeyVault.Fluent.KeyImpl.CreateRawAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.KeyVault.Fluent.KeyImpl.CreateResourceAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.ResourceActions.Creatable4.Microsoft.Azure.Management.ResourceManager.Fluent.Core.ResourceActions.IResourceCreator.CreateResourceAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.DAG.CreatorTaskItem1.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.DAG.TaskGroupBase1.ExecuteNodeTaskAsync(DAGNode1 node, CancellationToken cancellationToken) at Deployment.PreProvisioning.HostedService.<>c__DisplayClass5_0.<<CreateAzureKeyVault>b__1>d.MoveNext() in C:\git\xyz123\src\release\Deployment.PreProvisioning\Program.cs:line 206 --- End of stack trace from previous location where exception was thrown --- at Deployment.Core.ConsoleEx.LabeledAction(String label, Func1 action) in C:\git\xyz123\src\release\Deployment.Core\ConsoleEx.cs:line 75 Microsoft.Rest.RestException: Cannot communicate with server. No authentication token available for 'https://vault.azure.net/'. at Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Microsoft.Azure.KeyVault.KeyVaultClient.CreateKeyWithHttpMessagesAsync(String vaultBaseUrl, String keyName, String kty, Nullable1 keySize, IList1 keyOps, KeyAttributes keyAttributes, IDictionary2 tags, String curve, Dictionary2 customHeaders, CancellationToken cancellationToken) at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.CreateKeyAsync(IKeyVaultClient operations, String vaultBaseUrl, String keyName, String kty, Nullable1 keySize, IList1 keyOps, KeyAttributes keyAttributes, IDictionary2 tags, String curve, CancellationToken cancellationToken) at Microsoft.Azure.Management.KeyVault.Fluent.KeyImpl.CreateRawAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.KeyVault.Fluent.KeyImpl.CreateResourceAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.ResourceActions.Creatable4.Microsoft.Azure.Management.ResourceManager.Fluent.Core.ResourceActions.IResourceCreator.CreateResourceAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.DAG.CreatorTaskItem1.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.DAG.TaskGroupBase1.ExecuteNodeTaskAsync(DAGNode1 node, CancellationToken cancellationToken) at Deployment.PreProvisioning.HostedService.<>c__DisplayClass5_0.<<CreateAzureKeyVault>b__1>d.MoveNext() in C:\git\xyz123\src\release\Deployment.PreProvisioning\Program.cs:line 206 --- End of stack trace from previous location where exception was thrown --- at Deployment.Core.ConsoleEx.LabeledAction(String label, Func1 action) in C:\git\xyz123\src\release\Deployment.Core\ConsoleEx.cs:line 75 at Deployment.PreProvisioning.HostedService.CreateAzureKeyVault(IAzure api, Environment environment, String resourceGroup, String resourceSuffix, String location, Dictionary`2 tags, CancellationToken cancellationToken) in C:\git\xyz123\src\release\Deployment.PreProvisioning\Program.cs:line 170 at Deployment.PreProvisioning.HostedService.StartAsync(CancellationToken cancellationToken) in C:\git\xyz123\src\release\Deployment.PreProvisioning\Program.cs:line 108

Setup (please complete the following information):

Any help is appreciated :)

Update: I could find out that when I use a Service Principal together with SdkContext.AzureCredentialsFactory.FromServicePrincipal, I can create Azure Key Vault Keys successfully. Whereas manually getting an instance of AzureCredentials by using AzureServiceTokenProvider is not working wether it using Azure CLI or Service Principal.

weidongxu-microsoft commented 4 years ago

@Rookian

I think the cause is here. https://github.com/Azure/azure-libraries-for-net/blob/master/src/ResourceManagement/ResourceManager/Authentication/AzureCredentials.cs#L129-L154

KeyVault data-plane client actually need a different adSettings.TokenAudience from management/graphrbac. If credential of service principal is provided, it will use it to obtain a token for KeyVault data-plane client. But if in the code, managementToken and graphToken is provided instead, code here cannot auth correctly.

One way to solve is e.g. using Microsoft.Azure.KeyVault lib directly, when handling key/secret, after vault is created via Fluent SDK.