Documentation Index
Fetch the complete documentation index at: https://aipkg.org/llms.txt
Use this file to discover all available pages before exploring further.
Overview
NuGet.Services.KeyVault is a shared library that wraps the Azure Key Vault SDK behind a clean abstraction layer, enabling NuGet services to retrieve and manage secrets without being coupled to the underlying Azure SDK types. It defines a set of interfaces (ISecretReader, ISecretWriter, ISecretInjector, and their caching variants) that allow consuming projects to work with secrets uniformly, whether the backing store is a real Key Vault vault, an in-memory cache, or a no-op stub used in local development and testing.
The library ships two distinct caching strategies built on top of ISecretReader. The first, CachingSecretReader, uses time-based expiry: secrets are held in a ConcurrentDictionary and re-fetched from Key Vault after a configurable interval (default 24 hours) or when they are within a configurable window of their Key Vault expiration date (default 30 minutes before expiry). The second, RefreshableSecretReader, separates the concern of when to refresh from the concern of reading: the cache is populated on first access and is only updated when Refresh() or RefreshAsync() is explicitly called, which suits long-running services that want to pre-warm secrets during startup and refresh them on a background timer.
Authentication with Key Vault is handled inside KeyVaultReader and is determined entirely by the KeyVaultConfiguration provided at construction time. Three modes are supported: managed identity (using ManagedIdentityCredential in production or DefaultAzureCredential in DEBUG builds), client certificate with standard authentication (ClientCertificateCredential), and client certificate with the SendX5c flag set, which instructs Azure Active Directory to include the full certificate chain in the authentication request. The SecretClient is created lazily so that configuration errors surface at first use rather than at startup.
Role in System
ISecretReader, ISecretInjector) rather than on concrete types, which allows the caching and refresh strategy to be swapped or composed at the DI registration site without changing call sites.
Two Caching Strategies
CachingSecretReader refreshes automatically on a time-based schedule. RefreshableSecretReader only refreshes when explicitly told to, making it safe for ASP.NET startup scenarios where async vault calls can cause deadlocks.Secret Injection
SecretInjector scans arbitrary strings for tokens framed with $$ (e.g., $$MySecretName$$) and replaces them with the resolved secret value, enabling secrets to be embedded in configuration strings.Three Auth Modes
Supports managed identity, client certificate, and client certificate with full chain (
SendX5c). In DEBUG builds, managed identity falls back to DefaultAzureCredential for developer workstation convenience.Null-Object Pattern
EmptySecretReader returns the secret name itself as its value, providing a safe no-op implementation for local development and unit tests that do not need real vault connectivity.Key Files and Classes
| File | Class / Type | Purpose |
|---|---|---|
ISecret.cs | ISecret | Core contract: name, string value, and optional DateTimeOffset expiration. |
ISecretReader.cs | ISecretReader | Synchronous and async read operations returning raw strings or ISecret objects. |
ISecretWriter.cs | ISecretWriter | Extends ISecretReader with SetSecretAsync, adding optional expiration on write. |
ISecretInjector.cs | ISecretInjector | Contract for replacing framed tokens in a string with their resolved secret values. |
ISecretReaderFactory.cs | ISecretReaderFactory | Factory that creates an ISecretReader and an ISecretInjector from that reader. |
IRefreshableSecretReaderFactory.cs | IRefreshableSecretReaderFactory | Extends ISecretReaderFactory with explicit Refresh() / RefreshAsync() control. |
ICachingSecretReader.cs | ICachingSecretReader | Adds TryGetCachedSecret and TryGetCachedSecretObject for non-blocking cache inspection. |
ICachingSecretInjector.cs | ICachingSecretInjector | Extends ISecretInjector with TryInjectCached, which only succeeds if all secrets are already cached. |
KeyVaultConfiguration.cs | KeyVaultConfiguration | Immutable configuration record. Two constructors: one for managed identity, one for certificate auth. |
KeyVaultSecret.cs | KeyVaultSecret | Concrete ISecret implementation wrapping name, value, and expiration. |
KeyVaultReader.cs | KeyVaultReader | Calls the Azure SecretClient directly; lazily constructs the client using the configured auth mode. |
KeyVaultWriter.cs | KeyVaultWriter | Extends KeyVaultReader and adds SetSecretAsync by calling SecretClient.SetSecretAsync. |
CachingSecretReader.cs | CachingSecretReader | Time-based caching layer (default 24 h TTL; refreshes 30 min before Key Vault expiry). |
CachingSecretReaderFactory.cs | CachingSecretReaderFactory | Decorator factory that wraps another ISecretReaderFactory and returns CachingSecretReader instances. |
RefreshableSecretReader.cs | RefreshableSecretReader | Cache-first reader where cache is only populated on first read and refreshed on explicit call. |
RefreshableSecretReaderFactory.cs | RefreshableSecretReaderFactory | Factory that shares a single ConcurrentDictionary cache across all created RefreshableSecretReader instances. |
RefreshableSecretReaderSettings.cs | RefreshableSecretReaderSettings | Mutable settings class with BlockUncachedReads flag to prevent live vault calls during request handling. |
SecretInjector.cs | SecretInjector | Parses $$token$$ patterns in strings and resolves each token via the underlying ISecretReader. Also implements TryInjectCached via ICachingSecretReader when available. |
EmptySecretReader.cs | EmptySecretReader | No-op ICachingSecretReader that echoes the secret name back as its value; used in tests and local dev. |
CertificateUtility.cs | CertificateUtility | Static helpers to locate an X509Certificate2 by thumbprint or by subject distinguished name from the Windows certificate store. |
Dependencies
NuGet Package References
| Package | Purpose |
|---|---|
Azure.Core | Base Azure SDK types including TokenCredential. |
Azure.Identity | Provides ManagedIdentityCredential, DefaultAzureCredential, and ClientCertificateCredential. |
Azure.Security.KeyVault.Secrets | SecretClient used for all Key Vault read and write operations. |
Microsoft.Extensions.Logging.Abstractions | ILogger accepted on all read methods for optional diagnostic logging. |
Newtonsoft.Json | Pulled in as a transitive dependency requirement; not directly used by this project’s code. |
System.Text.Json | Pulled in as a transitive dependency requirement; not directly used by this project’s code. |
Internal Project References
This project has no internal project references. It is a leaf library.| Project | Purpose |
|---|---|
NuGet.Services.Configuration | References this project to inject Key Vault secrets into configuration strings. |
NuGet.Services.Sql | References this project to retrieve database connection string secrets from Key Vault. |
NuGetGallery | References this project for application-wide secret retrieval at startup and runtime. |
SplitLargeFiles | References this project for vault access in the CLI tool. |
Notable Patterns and Implementation Details
The
SecretClient inside KeyVaultReader is wrapped in a Lazy<T>. The client is not created until the first secret read call, which means authentication failures (bad certificate, wrong tenant ID) appear at first use, not at application startup.CachingSecretReader uses two independent freshness criteria. A cached secret is considered outdated if either (a) more than refreshIntervalSec seconds have passed since it was cached, or (b) the secret’s Key Vault expiration timestamp is within refreshIntervalBeforeExpirySec seconds of the current UTC time. This prevents serving an expired secret value even if the time-based TTL has not yet elapsed.RefreshableSecretReaderFactory shares a single ConcurrentDictionary<string, ISecret> instance across all ISecretReader objects it creates. This means calling RefreshAsync on the factory refreshes secrets for every consumer that was given a reader from that factory instance.CertificateUtility.FindLatestActiveCertificateBySubject selects the certificate with the latest NotAfter date when multiple certificates share the same subject distinguished name, with NotBefore as a tiebreaker. This supports rolling certificate deployments where both the old and new certificates exist in the store simultaneously.