Features
Vault.Extension.Configuration provides comprehensive integration with HashiCorp Vault for .NET applications. This page details all available features and capabilities.
🔐 Multiple Authentication Methods
The library supports three authentication methods to fit different deployment scenarios:
Local Token Authentication
Ideal for development environments where a Vault token file is available:
using Vault.Extentions;
using Vault.Options;
using Vault.Enum;
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Local,
Configuration = new VaultLocalConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret",
TokenFilePath = "~/.vault-token",
IgnoreSslErrors = false
}
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "development");
Configuration Properties:
VaultUrl: Vault server URLMountPoint: KV secrets engine mount pointTokenFilePath: Path to the token file (supports environment variable expansion like~/)IgnoreSslErrors: Disable SSL validation (use only for development)
AWS IAM Authentication
Perfect for applications running on AWS infrastructure (EC2, ECS, EKS, Lambda):
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.AWS_IAM,
Configuration = new VaultAwsConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "myapp",
Environment = "production",
AwsAuthMountPoint = "aws",
AwsIamRoleName = "myapp-production-role" // Optional
}
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");
Configuration Properties:
VaultUrl: Vault server URLMountPoint: KV secrets engine mount pointEnvironment: Environment name (used for role naming)AwsAuthMountPoint: AWS auth method mount point (default:"aws")AwsIamRoleName: IAM role name (optional, defaults to{MountPoint}-{Environment}-role)IgnoreSslErrors: Disable SSL validation
How it works:
- Automatically retrieves AWS credentials from the instance metadata service
- Signs the authentication request with instance IAM role
- No hardcoded credentials needed in your application
Custom Authentication
For authentication methods not natively supported (AppRole, LDAP, UserPass, Kubernetes, etc.):
using VaultSharp.V1.AuthMethods.AppRole;
using VaultSharp.V1.AuthMethods.UserPass;
// AppRole example
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Custom,
Configuration = new VaultDefaultConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret"
},
CustomAuthMethodInfo = new AppRoleAuthMethodInfo("role-id", "secret-id")
};
// UserPass example
var vaultOptionsUserPass = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Custom,
Configuration = new VaultDefaultConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "secret"
},
CustomAuthMethodInfo = new UserPassAuthMethodInfo("username", "password")
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");
⚙️ Seamless Configuration Integration
IConfiguration Access
Secrets are automatically loaded into the ASP.NET Core configuration system, making them accessible just like appsettings.json values:
public class DatabaseService
{
private readonly IConfiguration _configuration;
public DatabaseService(IConfiguration configuration)
{
_configuration = configuration;
}
public void Connect()
{
// Access Vault secrets like any other configuration value
var connectionString = _configuration["Database:ConnectionString"];
var password = _configuration["Database:Password"];
var timeout = _configuration.GetValue<int>("Database:Timeout", 30);
Console.WriteLine($"Connecting to database with timeout {timeout}s");
}
}
Key Features:
- Hierarchical keys using colon notation (
Database:Password) - Automatic type conversion with
GetValue<T> - Fallback values for missing keys
- Secrets override appsettings.json values (by default)
IOptions Pattern Support
Bind Vault secrets to strongly-typed configuration classes with full validation support:
// Define your settings class
public class EmailSettings
{
public string SmtpServer { get; set; } = string.Empty;
public int Port { get; set; } = 587;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; // From Vault
}
// In Program.cs
builder.Services.Configure<EmailSettings>(
builder.Configuration.GetSection("Email"));
// Use in your services
public class EmailService
{
private readonly EmailSettings _settings;
public EmailService(IOptions<EmailSettings> options)
{
_settings = options.Value;
}
public void SendEmail()
{
// Use settings with full IntelliSense support
var client = new SmtpClient(_settings.SmtpServer, _settings.Port)
{
Credentials = new NetworkCredential(_settings.Username, _settings.Password)
};
}
}
Benefits:
- Strong typing with compile-time safety
- IntelliSense support in your IDE
- Works with
IOptionsMonitor<T>for configuration reloading - Compatible with data annotations validation
🔄 Direct Vault Access with IVaultService
For dynamic secret retrieval at runtime, inject and use IVaultService:
using Vault.Abstractions;
public class SecretManager
{
private readonly IVaultService _vaultService;
private readonly ILogger<SecretManager> _logger;
public SecretManager(IVaultService vaultService, ILogger<SecretManager> logger)
{
_vaultService = vaultService;
_logger = logger;
}
// Get a specific secret value
public async Task<string?> GetApiKeyAsync(string environment)
{
var value = await _vaultService.GetSecretValueAsync(environment, "ApiKey");
return value?.ToString();
}
// Get all secrets for an environment
public async Task<Dictionary<string, object>> LoadEnvironmentSecretsAsync(string environment)
{
_logger.LogInformation("Loading secrets for environment: {Environment}", environment);
return await _vaultService.GetSecretsAsync(environment);
}
// Get nested secret using dot notation
public async Task<string?> GetNestedSecretAsync()
{
// Access nested structure: { "Api": { "Settings": { "Key": "value" } } }
var value = await _vaultService.GetNestedSecretValueAsync("production", "Api.Settings.Key");
return value?.ToString();
}
// List all available environments
public async Task<IEnumerable<string>> GetAvailableEnvironmentsAsync()
{
return await _vaultService.ListEnvironmentsAsync();
}
// Refresh secrets at runtime
public async Task RefreshSecretsAsync(string environment)
{
var secrets = await _vaultService.GetSecretsAsync(environment);
_logger.LogInformation("Loaded {Count} secrets", secrets.Count);
// Process updated secrets
}
}
IVaultService Methods
| Method | Description | Returns |
|---|---|---|
ListEnvironmentsAsync() |
Lists all environment paths in Vault | IEnumerable<string> |
GetSecretsAsync(string environment) |
Retrieves all secrets for an environment | Dictionary<string, object> |
GetSecretValueAsync(string environment, string key) |
Gets a specific secret value | object? |
GetNestedSecretValueAsync(string environment, string path) |
Gets nested secret using dot notation | object? |
Use Cases for IVaultService:
- Dynamic secret retrieval during application runtime
- Refreshing secrets without restarting the application
- Loading secrets based on user input or business logic
- Querying available environments
- Accessing nested JSON structures in secrets
✅ Automatic Configuration Validation
Configuration is validated using FluentValidation on startup:
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.Local,
Configuration = new VaultLocalConfiguration
{
// Missing VaultUrl - will throw VaultConfigurationException
MountPoint = "secret",
TokenFilePath = "~/.vault-token"
}
};
// Throws VaultConfigurationException with detailed error message
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");
Validated Rules:
- Required fields:
VaultUrl,MountPoint,TokenFilePath(for Local), etc. - Configuration type must match authentication type
- File paths are validated for Local authentication
- Custom auth method info is required when using Custom authentication
- Environment name cannot be empty
Validation Errors Example:
VaultConfigurationException:
- 'Vault Url' must not be empty.
- 'Token File Path' is required for Local authentication.
🏗️ Full Dependency Injection Support
The library integrates seamlessly with ASP.NET Core's DI container:
Registration
using Vault.Extentions;
var builder = WebApplication.CreateBuilder(args);
// Register Vault services
var vaultOptions = new VaultOptions { /* ... */ };
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");
// Register your services that depend on Vault
builder.Services.AddScoped<SecretManager>();
builder.Services.AddSingleton<CacheService>();
var app = builder.Build();
// Initialize Vault providers
app.UseVault();
Injection
// Inject IVaultService
public class ApiController : ControllerBase
{
private readonly IVaultService _vaultService;
public ApiController(IVaultService vaultService)
{
_vaultService = vaultService;
}
[HttpGet("secrets")]
public async Task<IActionResult> GetSecrets()
{
var secrets = await _vaultService.GetSecretsAsync("production");
return Ok(new { count = secrets.Count });
}
}
// Inject IConfiguration
public class DatabaseFactory
{
private readonly IConfiguration _configuration;
public DatabaseFactory(IConfiguration configuration)
{
_configuration = configuration;
}
public DbContext CreateContext()
{
var connectionString = _configuration["Database:ConnectionString"];
return new MyDbContext(connectionString);
}
}
// Inject IOptions
public class EmailService
{
private readonly EmailSettings _settings;
public EmailService(IOptions<EmailSettings> options)
{
_settings = options.Value;
}
}
🔒 Security Features
SSL Certificate Validation
By default, SSL certificates are validated. Only disable for development:
var vaultOptions = new VaultOptions
{
Configuration = new VaultLocalConfiguration
{
VaultUrl = "https://vault.local",
MountPoint = "secret",
TokenFilePath = "~/.vault-token",
// ONLY for development!
IgnoreSslErrors = builder.Environment.IsDevelopment()
}
};
Production Recommendation:
- Always use valid SSL certificates
- Configure your system to trust Vault's CA if using internal PKI
- Never set
IgnoreSslErrors = truein production
Secure Token Handling
For Local authentication, tokens are handled securely:
- Token file content is never logged
- File is read once at startup
- Supports environment variable expansion (
~/.vault-token,%USERPROFILE%\.vault-token) - File permissions are respected by the OS
Least Privilege Access
The library only requires read access to Vault secrets:
# Example Vault policy
path "secret/data/production/*" {
capabilities = ["read", "list"]
}
path "secret/metadata/production/*" {
capabilities = ["read", "list"]
}
🎯 Environment-Based Secret Management
Organize secrets by environment for proper isolation:
var environment = builder.Environment.EnvironmentName; // "Development", "Staging", "Production"
var vaultOptions = new VaultOptions
{
AuthenticationType = VaultAuthenticationType.AWS_IAM,
Configuration = new VaultAwsConfiguration
{
VaultUrl = "https://vault.example.com",
MountPoint = "myapp",
Environment = environment.ToLower()
}
};
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: environment.ToLower());
Vault Structure:
secret/
├── development/
│ ├── Database:ConnectionString
│ └── Api:Key
├── staging/
│ ├── Database:ConnectionString
│ └── Api:Key
└── production/
├── Database:ConnectionString
└── Api:Key
📦 Exception Handling
The library provides specific exception types for detailed error handling:
using Vault.Exceptions;
try
{
var vaultOptions = new VaultOptions { /* ... */ };
builder.Services.AddVault(builder.Configuration, vaultOptions, environment: "production");
var app = builder.Build();
app.UseVault();
}
catch (VaultConfigurationException ex)
{
// Configuration validation errors (missing fields, wrong types, etc.)
_logger.LogError(ex, "Vault configuration is invalid");
throw;
}
catch (VaultAuthenticationException ex)
{
// Authentication failures (invalid token, insufficient permissions)
_logger.LogError(ex, "Failed to authenticate with Vault");
throw;
}
catch (FileNotFoundException ex) when (ex.Message.Contains("Token file"))
{
// Token file not found (Local authentication only)
_logger.LogError(ex, "Vault token file not found");
throw;
}
catch (HttpRequestException ex)
{
// Network errors (Vault server unreachable)
_logger.LogError(ex, "Cannot connect to Vault server");
throw;
}
🚀 Advanced Features
Configuration Loading Order
Vault secrets are loaded after other configuration sources, so they override:
- appsettings.json
- appsettings.{Environment}.json
- User secrets (development only)
- Environment variables
- Vault secrets ← Highest priority
UseVault() Initialization
The UseVault() call is required to inject IVaultService into configuration providers:
var app = builder.Build();
// This initializes all Vault configuration providers
// Without this, secrets won't be loaded!
app.UseVault();
app.MapControllers();
app.Run();
What it does:
- Locates all
VaultConfigurationProviderinstances in the configuration - Injects the registered
IVaultService - Triggers secret loading from Vault
Hierarchical Configuration Keys
Use colon notation for hierarchical keys:
// In Vault: "Database:ConnectionString" = "Server=..."
// Access in code:
var connectionString = _configuration["Database:ConnectionString"];
// Or bind to nested class:
public class AppSettings
{
public DatabaseConfig Database { get; set; }
}
public class DatabaseConfig
{
public string ConnectionString { get; set; }
}
📊 Comparison: When to Use Each Method
| Method | Best For | Pros | Cons |
|---|---|---|---|
| IConfiguration | Static secrets needed at startup | Simple, familiar API | No type safety |
| IOptions |
Strongly-typed configuration | Type safety, IntelliSense, validation | Requires class definition |
| IVaultService | Dynamic runtime access | Flexible, can refresh secrets | More code, async |
Recommendation:
- Use IConfiguration for simple access
- Use IOptions
for production applications with validation - Use IVaultService when you need to refresh or dynamically query secrets