12

I am having trouble accessing a secret from an Azure key vault. I suspect the problem is that I don't adequately understand the terminology, so the arguments I'm supplying to various API calls are wrong.

Here's the basic code I'm using:

    protected async Task<string> GetCommunityKeyAsync( UserConfiguration user )
    {
        var client = new KeyVaultClient( 
            new KeyVaultClient.AuthenticationCallback( GetAccessTokenAsync ),
            new HttpClient() );

        // user.VaultUrl is the address of my key vault
        // e.g., https://previously-created-vault.vault.azure.net
        var secret = await client.GetSecretAsync( user.VaultUrl, "key-to-vault-created-in-azure-portal" );

        return secret.Value;
    }

    private async Task<string> GetAccessTokenAsync( string authority, string resource, string scope )
    {
        var context = new AuthenticationContext( authority, TokenCache.DefaultShared );

        // this line throws a "cannot identify user exception; see
        // below for details
        var result =
            await context.AcquireTokenAsync( resource, "id-of-app-registered-via-azure-portal", new UserCredential() );

        return result.AccessToken;
    }

Here is the exception that gets thrown:

Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException
HResult=0x80131500 Message=unknown_user: Could not identify logged in user Source=Microsoft.IdentityModel.Clients.ActiveDirectory
StackTrace: at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenNonInteractiveHandler.d__4.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase.d__57.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.d__37.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions.d__0.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at NextDoorScanner.ScannerJob.<GetAccessTokenAsync>d__21.MoveNext() in C:\Programming\CommunityScanner\CommunityScanner\ScannerJob.cs:line 197 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Azure.KeyVault.KeyVaultCredential.d__9.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Azure.KeyVault.KeyVaultCredential.<ProcessHttpRequestAsync>d__10.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.KeyVault.KeyVaultClient.<GetSecretWithHttpMessagesAsync>d__65.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable
1.ConfiguredTaskAwaiter.GetResult() at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.d__11.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at NextDoorScanner.ScannerJob.<GetCommunityKeyAsync>d__20.MoveNext() in C:\Programming\CommunityScanner\CommunityScanner\ScannerJob.cs:line 188 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at NextDoorScanner.NextDoorScannerJob.d__4.MoveNext() in C:\Programming\CommunityScanner\CommunityScanner\NextDoorScannerJob.cs:line 46 at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at NextDoorScanner.Program.Main(String[] args) in C:\Programming\CommunityScanner\CommunityScanner\Program.cs:line 22

I did some configuration, I thought involving registering my desktop as an Azure user, via powershell:

Login-AzureRmAccount
// as I recall, this next line complained about the app ID already being   defined
New-AzureRmADServicePrincipal -ApplicationId 'id-of-app-previously-defined-via-azure-portal'
Set-AzureRmKeyVaultAccessPolicy -VaultName 'vault-name' -ServicePrincipalName id-of-app-previously-defined-via-azure-portal -PermissionsToSecrets Get

I'm unclear if I'm supposed to be providing the vault key to GetSecretAsync(). I also wonder if I'm supposed to be doing something other than passing a newly-created UserCredential to AcquireTokenAsync(). Finally, I see references online to creating a storage account for use with key vaults, which I did, but I didn't create the vault I'm using "in" a storage account. And I'm not identifying the storage account in the code.

Help, or a reference to a really good example accessing key vaults from a console desktop app would be appreciated.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Mark Olbert
  • 6,584
  • 9
  • 35
  • 69

4 Answers4

25

Mark's blog was extremely helpful, from that blog I learnt how to do it and below are the steps and code as of 6-Nov-2018.

Summary of the steps:

  1. Register App
  2. Create Key inside this newly registered App
  3. Create Key Vault and Assign permission to the app
  4. Create Secret inside the vault

Access them thru code

using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Experiments.AzureKeyValut
{
    internal class AzureKeyValueDemo
    {
        private static async Task Main(string[] args)
        {
            await GetSecretAsync("https://YOURVAULTNAME.vault.azure.net/", "YourSecretKey");
        }

        private static async Task<string> GetSecretAsync(string vaultUrl, string vaultKey)
        {
            var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAccessTokenAsync), new HttpClient());
            var secret = await client.GetSecretAsync(vaultUrl, vaultKey);

            return secret.Value;
        }

        private static async Task<string> GetAccessTokenAsync(string authority, string resource, string scope)
        {
            //DEMO ONLY
            //Storing ApplicationId and Key in code is bad idea :)
            var appCredentials = new ClientCredential("YourApplicationId", "YourApplicationKey");
            var context = new AuthenticationContext(authority, TokenCache.DefaultShared);

            var result = await context.AcquireTokenAsync(resource, appCredentials);

            return result.AccessToken;
        }
    }
}

How to register your app:

How to register your app in Azure

How to create Azure App's password and get your App's Id

How to create your App's password and get your App's Id

How to create Azure Key Vault and Assign Permissions

How to create Azure Key Vault and Assign Permissions

How to create Azure secrets

How to create Azure secrets

How to access it thru code

enter image description here

ndd
  • 3,051
  • 4
  • 25
  • 40
  • 4
    I don't understand the "ClientId and Secret" approach. Ok, I'm not exposing in config file my actual secret (let's say for example a service password), but I'm exposing in config file a ClientId and a Secret that everyone can see and then use to access the key vault and then retrieve my actual secret. So, I am just moving the problem one step further but not solving it, correct? – Giacomo Pirinoli Nov 17 '20 at 16:04
9

In addition to what Tom provided, after I finally figured out how to get things to work I documented what I learned over at https://jumpforjoysoftware.com/2017/12/azure-key-vaults/. Hopefully this will all save people some serious frustration.

Mark Olbert
  • 6,584
  • 9
  • 35
  • 69
  • This article helped me very much. Also, Azure lets you make access policies for vaults through the UI, the setting is called "Access Policies", so you can skip the powershell step from the article by going into the vault's blade and selecting "access policies" from the menu (not to discredit powershell as a whole). – Nomenator May 04 '18 at 20:39
  • Mark, do you have any information implementing this using certificates? – Juan May 21 '19 at 18:45
  • Sorry, @Juan, wish I could help but I don’t. – Mark Olbert May 22 '19 at 19:16
  • Don’t worry Mark, thanks for replying. I will continue my research, there is a new library version 3 to make this implementation easier(with certificates) – Juan May 22 '19 at 19:18
3

Help, or a reference to a really good example accessing key vaults from a console desktop app would be appreciated.

After we registry the Azure Directory App then we need to assign role to application. if we want to operate Azure Key Vault, we also need to give permission to operate Key Vault. The resource for key vault is https://vault.azure.net. You also could get more detail info from another SO thread.

Demo code:

 static string appId = "application Id";
 static string tenantId = "tenant id";
 static string uri = "http://localhost:13526"; //redirect uri
 static void Main(string[] args)
 {
    var kv = new KeyVaultClient(GetAccessToken);
    var scret = kv.GetSecretAsync("https://xxxx.vault.azure.net", "xxxx").GetAwaiter().GetResult();
 }

 public static async Task<string> GetAccessToken(string azureTenantId,string clientId,string redirectUri)
 {
       var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
       var tokenResult = await context.AcquireTokenAsync("https://vault.azure.net", appId, new Uri(uri), new PlatformParameters(PromptBehavior.SelectAccount));
       return tokenResult.AccessToken;
  }
Tom Sun - MSFT
  • 24,161
  • 3
  • 30
  • 47
0

Here are the docs and samples for how to do this using the latest version of the Azure SDK (Azure.Security.KeyVault.Secrets).

Just in case, here are the same docs for some of the other supported languages:

Java

JavaScript

Python

Christopher Scott
  • 2,676
  • 2
  • 26
  • 26