Skip to content

Enable extensibility via IEnumerable<ICredentialSourceLoader> constructor parameter #3485

@jmprieur

Description

@jmprieur

(co-authored with GitHub Copilot)
Is your feature request related to a problem? Please describe.
Currently, both DefaultCredentialsLoader and DefaultCertificateLoader have constructors that only initialize built-in credential loaders. This limits extensibility, as custom credential loaders cannot be injected directly via the constructor.

Describe the solution you'd like
Update the constructors of DefaultCredentialsLoader and DefaultCertificateLoader to accept an IEnumerable<ICredentialSourceLoader> parameter. This allows consumers to provide custom loaders and override or extend the built-in ones. The default constructor should still provide backwards compatibility.

Describe alternatives you've considered

  • Subclassing and injecting custom loaders manually after instantiation, but this is less clean and doesn't work with DI frameworks.
  • Factory or builder patterns, but constructor injection is the most straightforward for extensibility and testing.

Additional context
This change would enable scenarios where credential sources are dynamically added, such as integration with external secret providers or custom certificate stores. It also improves testability by allowing mocking of credential source loaders.

Reference:

  • See line 27 in DefaultCredentialsLoader.cs and similar code in DefaultCertificateLoader.
  • Example update:
public DefaultCredentialsLoader(
    ILogger<DefaultCredentialsLoader>? logger,
    IEnumerable<ICredentialSourceLoader>? credentialSourceLoaders = null)
{
    // ...
}

Work to do:

  • Make the change to the DefaultCredentialLoader and DefaultCertificateLoader constructors that already have the most inputs
  • Update the code and the tests if needed.
  • Make sure that there are no breaking changes.

Idea of implementation:

public DefaultCredentialsLoader(
    ILogger<DefaultCredentialsLoader>? logger,
    IEnumerable<ICredentialSourceLoader>? credentialSourceLoaders = null)
{
    _logger = logger ?? new NullLogger<DefaultCredentialsLoader>();

    CredentialSourceLoaders = new Dictionary<CredentialSource, ICredentialSourceLoader>();

    // Add built-in loaders
    var builtInLoaders = new List<ICredentialSourceLoader>
    {
        new KeyVaultCertificateLoader(),
        new FromPathCertificateLoader(),
        new StoreWithThumbprintCertificateLoader(),
        new StoreWithDistinguishedNameCertificateLoader(),
        new Base64EncodedCertificateLoader(),
        new SignedAssertionFromManagedIdentityCredentialLoader(_logger),
        new SignedAssertionFilePathCredentialsLoader(_logger)
    };

    foreach (var loader in builtInLoaders)
    {
        CredentialSourceLoaders[loader.SourceType] = loader;
    }

    // Add additional/extensible loaders (can override built-ins)
    if (credentialSourceLoaders != null)
    {
        foreach (var loader in credentialSourceLoaders)
        {
            CredentialSourceLoaders[loader.SourceType] = loader;
        }
    }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions