Rakesh Kumar
Rakesh Kumar

Reputation: 3149

How to authenticate Azure Key Vault request using clientId and thumbprint in .NET 7 Web Api?

I have .NET 7 Web Api application where I m using the AddAzureKeyVault method to add Azure Key Vault configurations to my IConfigurationBuilder instance. Currently, I am using the DefaultAzureCredential for authentication.

However, I want to modify this method to authenticate the Key Vault request using clientId and thumbprint. I have access to these values and want to use them for authentication instead of the default credential types.

Here is my current code:

public static IConfigurationBuilder AddAzureKeyVaultConfiguration(this IConfigurationBuilder configurationBuilder, IWebHostEnvironment hostingEnvironment)
{
    if (!hostingEnvironment.IsEnvironment("Localhost"))
    {
        var builtConfig = configurationBuilder.Build();
        var keyVaultEndpoint = $"https://{builtConfig["KeyVaultConfigOption:Name"]}.vault.azure.net/";

        configurationBuilder.AddAzureKeyVault(
            new Uri(keyVaultEndpoint),
            new DefaultAzureCredential(new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential = true,
                ExcludeInteractiveBrowserCredential = true,
                ExcludeAzurePowerShellCredential = true,
                ExcludeSharedTokenCacheCredential = true,
                ExcludeVisualStudioCodeCredential = true,
                ExcludeVisualStudioCredential = true,
                ExcludeAzureCliCredential = false,
                ExcludeManagedIdentityCredential = false,
            })

            );
    }

    return configurationBuilder;
}

}

Program.cs

var builder = WebApplication.CreateBuilder(args);
{
    // Environment configuration
    var configuration = builder.Configuration;
    var env = builder.Environment;

    configuration.AddAppConfiguration(env);

    // Add azure key vault configuration
    builder.Configuration.AddAzureKeyVaultConfiguration(env);

    builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>());
    builder.Services.AddHttpClient();

    builder.Services.AddApplication();
    builder.Services.AddInfrastructure(builder.Configuration);

    builder.Services.AddHelpers();

    builder.Services.AddControllers()
       // Configures the JSON serialization options for controllers.
       .AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.PropertyNamingPolicy = null;
        });

    builder.Services.Configure<ApiBehaviorOptions>(options =>
    {
        options.SuppressModelStateInvalidFilter = true;
    });

}

var app = builder.Build();
{
    app.UseHttpsRedirection();

    // Authentication & Authorization
    app.UseAuthentication();
    app.UseAuthorization();
    app.MapControllers();

    app.UseRouting();
    app.UseSwaggerWithVersioning();
}

if (app.Environment.IsDevelopment())
{
    //DEV configurations
}

app.Run();

appsettings.json

 "KeyVaultConfigOption": {
    "Name": "{KeyValultName}",
    "Url": "https://{KeyVaultName}.vault.azure.net",
    "Thumbprint": "",
    "ClientId": ""
  }

How can I modify this method to authenticate the Azure Key Vault request using ClientId and Thumbprint?

Upvotes: 0

Views: 598

Answers (2)

Rakesh Kumar
Rakesh Kumar

Reputation: 3149

As Pramod suggested in the previous answer, I made the following changes accordingly and now I am able to authenticate key vault request with certificates.

public static IConfigurationBuilder AddAzureKeyVaultConfiguration(this IConfigurationBuilder configurationBuilder, IWebHostEnvironment hostingEnvironment)
        {
            if (!hostingEnvironment.IsEnvironment("Development"))
            {
                var builtConfig = configurationBuilder.Build();
                var keyVaultEndpoint = $"https://{builtConfig["KeyVaultConfigOption:Name"]}.vault.azure.net/";

                var clientId = builtConfig["KeyVaultConfigOption:ClientId"];
                var thumbprint = builtConfig["KeyVaultConfigOption:Thumbprint"];
                var tenantId = builtConfig["KeyVaultConfigOption:TenantId"];

                var certificate = GetCertificate(thumbprint);

                var clientCertificateCredential = new ClientCertificateCredential(tenantId, clientId, certificate);


                configurationBuilder.AddAzureKeyVault(new Uri(keyVaultEndpoint), clientCertificateCredential);

                var keyVaultClient = new SecretClient(new Uri(keyVaultEndpoint), clientCertificateCredential);

                Task.Run(() => LoadKeysFromKeyVault(configurationBuilder, keyVaultClient)).Wait();
            }

            return configurationBuilder;
        }



        private static X509Certificate2 GetCertificate(string thumbprint)
        {
            var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);

            store.Open(OpenFlags.ReadOnly);

            var cert = store.Certificates.OfType<X509Certificate2>()
                .FirstOrDefault(x => x.Thumbprint == thumbprint);
            store.Close();

            if (cert == null)
                throw new InvalidOperationException($"Failed to find the certificate for thumbprint:{thumbprint}");

            return cert;
        }

        private static async Task LoadKeysFromKeyVault(IConfigurationBuilder configurationBuilder, SecretClient keyVaultClient)
        {
            var serviceBusConfig = configurationBuilder.Build().GetSection(ServiceBusConfigOption.SectionName).Get<ServiceBusConfigOption>();

            var propertiesToLoad = new Dictionary<string, string>
            {
                { $"{ServiceBusConfigOption.SectionName}:{nameof(serviceBusConfig.ConnectionString)}", serviceBusConfig.ConnectionString },

            };

            foreach (var kvp in propertiesToLoad)
            {
                var configurationKey = kvp.Key;
                var secretName = kvp.Value;

                var secret = await keyVaultClient.GetSecretAsync(secretName);

                // Modify the configuration to replace the key value
                configurationBuilder.Sources.RemoveAt(configurationBuilder.Sources.Count - 1); // Remove the Key Vault source
                configurationBuilder.AddInMemoryCollection(new Dictionary<string, string>
                {
                    { configurationKey, secret.Value.Value }
                });
            }
        }

Upvotes: 0

PramodValavala
PramodValavala

Reputation: 6647

You can use the ClientCertificateCredential along with the X509Certificate2 object.

To first fetch the certificate required, use the thumbprint to find the certificate from the X509Store.

Upvotes: 1

Related Questions