How to Secure Your .NET Application Secrets with Azure Key Vault (Free Code Repository Included)

KristiadhyKristiadhy
6 min read

Securing your application is essential, especially in production environments. Sensitive information—such as application secrets (e.g., JWT secret keys, database connection strings), certificates, and encryption keys—can pose serious risks if exposed. It’s important to use secure storage solutions to protect these critical assets. One of the most robust options available is Azure Key Vault, which provides a safe and centralized way to manage your application's secrets.

Why use Azure Key Vault?

Your secret is stored safely in the cloud.

By avoiding the storage of security information within applications, you eliminate the need to embed sensitive data in the code. For instance, if an application needs to connect to a database, you can securely store the connection string in Key Vault instead of hardcoding it in the application.

Access to your Key Vault is also secured through both authentication and authorization. Before a caller (whether a user or an application) can access the Key Vault, they must first be properly authenticated and authorized. Authentication is handled through Microsoft Entra ID, while authorization can be managed either using Azure Role-Based Access Control (Azure RBAC) or Key Vault access policies.

Easy to monitor.

Azure Key Vault makes it easy to monitor how and when your secrets are accessed. By enabling logging for your vaults, you can track all activity in detail. You have full control over your logs — you can secure them by restricting access and deleting any logs you no longer need.

Automatic Secret Refresh Without Application Restart.

Another powerful advantage of using Azure Key Vault is its ability to automatically refresh secrets when they change, without requiring you to restart your application.

For example, if you store a JWT secret key in Key Vault and later decide to update it (e.g., for security rotation), your application can pick up the updated secret automatically. This eliminates downtime and reduces the risk associated with manually restarting services to load new secrets.

By combining Azure Key Vault with services like Azure App Configuration and enabling dynamic refresh policies, you can ensure that your application always uses the latest secure values, improving both security and operational efficiency.

Tip: To automatically see secret updates without restarting services, use IOptionsSnapshot<T> instead of IOptions<T> for your settings injection

Create and configure Azure Key Vault

Now, let’s jump into the implementation.

  • Go to https://portal.azure.com/ , select a service named Key Vaults, and create it.

  • In the side menu, select Objects > Secrets.

  • At the top, select the generate/import menu

  • Fill the name and secret value field, and then click the create button.

Note: In the screenshot below, I use "JwtSecret" as the secret name. However, in the example code, I use "JwtSettings--Secret"*.
Don’t let the difference confuse you — the concept remains the same.*

  • You can manage access permissions using Access Control (IAM).

However, in this article, we won't dive deeper into configuring Access Control settings.

Now that your secrets are securely set up, it's time to integrate them into your .NET application.

Configure your .NET application.

There are two ways to connect your .NET application to Azure Key Vault: Automatic Configuration Approach and the Manual Service Approach.

Automatic Configuration Approach.

With this approach, you load secrets from Azure Key Vault directly into your application's configuration.

To do this, you need to install 3 packages (The example is using .NET CLI. You can also install using NuGet Package):

dotnet add package Azure.Identity
dotnet add package Azure.Security.KeyVault.Secrets
dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets

In the example below, I’ll show you how to configure your appsettings.json and Program.cs
Note: For better code organization, I recommend moving the setup into a separate method to keep your Program.cs clean. I’ve provided a full example in the repository linked at the end of this article.

appsettings.json

"AzureKeyVault": {
  "VaultURI": "Your vault URI",
  "ClientId": "Your client id",
  "TenantId": "Your tenant id",
  "ClientSecret": "Your client secret"
}

Program.cs

var keyVaultSettings = new
{
  VaultUri = builder.Configuration["AzureKeyVault:VaultUri"],
  TenantId = builder.Configuration["AzureKeyVault:TenantId"],
  ClientId = builder.Configuration["AzureKeyVault:ClientId"],
  ClientSecret = builder.Configuration["AzureKeyVault:ClientSecret"]
}; // Taken from appsettings.json

// Add services to the container.
builder.Configuration.AddAzureKeyVault(
          new Uri(keyVaultSettings.VaultUri),
          new ClientSecretCredential(
              tenantId: keyVaultSettings.TenantId,
              clientId: keyVaultSettings.ClientId,
              clientSecret: keyVaultSettings.ClientSecret),
          new AzureKeyVaultConfigurationOptions
          {
            ReloadInterval = TimeSpan.FromMinutes(30)
          }); // Custom extension method to add Azure

What’s interesting here is that you can seamlessly combine values from both your appsettings.json and Azure Key Vault.

For example, if your local appsettings.json contains a key like JwtSettings:Secret, and you have a corresponding secret in Azure Key Vault named JwtSettings--Secret (In key vault, use a double dash -- to map to the colon :), the value from Azure Key Vault will automatically override the value from your local configuration.

Now, when you access your app configuration using the name of your secret — for example, configuration["JwtSettings:Secret"] — you will retrieve the value from Azure Key Vault.

Manual Service Approach.

This approach uses a custom service to retrieve secrets. First, you need to install 2 packages:

dotnet add package Azure.Identity
dotnet add package Azure.Security.KeyVault.Secrets

Next, I create a method to validate the credentials needed to access Azure Key Vault.

public static SecretClient CreateSecretClient(IConfiguration configuration)
{
    // Bind the configuration section to the AzureKeyVaultConfig class
    var keyVaultSetting = configuration.GetSection("AzureKeyVault").Get<AzureKeyVaultConfig>()
                   ?? throw new InvalidOperationException("Missing AzureKeyVault configuration.");
     var credential = new ClientSecretCredential(keyVaultSetting.TenantId,
                            keyVaultSetting.ClientId, 
                            keyVaultSetting.ClientSecret);
    var vaultUri = new Uri(keyVaultSetting.VaultUri);
     return new SecretClient(vaultUri, credential);
}

Next, I create a service that retrieves the secrets by using the SecretClient.

public async Task<string> GetJwtSecretAsync()
{
     var response = await _secretClient.GetSecretAsync("JwtSettings--Secret");
     return response.Value.Value;
}

Now you can call your secret using the GetJwtSecretAsync() method.

When accessing Azure Key Vault from your .NET application, there are two common ways to authenticate.
In the above example, I used TenantId, ClientId, and ClientSecret to authenticate with Azure Key Vault. However, there's another option that I recommend, which is using DefaultAzureCredential().
In the next section, I'll show you both authentication methods you can use to connect to your Azure Key Vault.

Two Ways to Authenticate to Azure Key Vault

When accessing Azure Key Vault from your .NET application, there are two common ways to authenticate:

1. Using DefaultAzureCredential()

DefaultAzureCredential is the easiest and most flexible option for many scenarios. It automatically tries multiple authentication methods under the hood, such as:

  • Environment variables

  • Managed Identity (for Azure-hosted apps)

  • Visual Studio / Azure CLI logged-in credentials (for local development)

This makes it a great choice if you want a seamless experience between local development and production environments, without changing any code.

Example:

var credential = new DefaultAzureCredential();
var client = new SecretClient(new Uri(keyVaultSettings.VaultUri), credential);

2. Using ClientSecretCredential

ClientSecretCredential is a more manual and explicit approach. You provide a Tenant ID, Client ID, and Client Secret directly, typically configured through your application settings.

This method is useful when you want full control over the credentials being used or when running in environments where Managed Identity is not available.

Example:

var credential = new ClientSecretCredential(
    tenantId: keyVaultSettings.TenantId,
    clientId: keyVaultSettings.ClientId,
    clientSecret: keyVaultSettings.ClientSecret);

var client = new SecretClient(new Uri(keyVaultSettings.VaultUri), credential);

🛠️ Let’s Get Hands-On!

I've uploaded the full implementation with a simple and clean approach — dive into the code here! Azure Key Vault Integration Repository

Happy coding!

0
Subscribe to my newsletter

Read articles from Kristiadhy directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kristiadhy
Kristiadhy

Experienced Full Stack .NET developer with a proven track record of designing and implementing robust business applications. Proficient in using ASP.NET Core Web API, Blazor, and WinForms to deliver high-quality, efficient code and scalable solutions. Strong focus on implementing industry best practices for cleaner and scalable outcomes.