5

I am working on .Net Core Web App with IdentityServer4 installed and configured on Startup.cs, Here I am loading the token signing certificate from a service that I already registered in the IOC:

        builder.AddSigningCredential(services.BuildServiceProvider().
        GetService<ISecretStoreService>().
        TokenSigningCertificate().Result);

Below the full code:

public void ConfigureServices(IServiceCollection services)
{
    //....

        var builder = services.AddIdentityServer(options =>
            {
                options.IssuerUri = Configuration.GetValue<string>("IdSrv:IssuerUri"); ;
                options.PublicOrigin = Configuration.GetValue<string>("IdSrv:PublicOrigin"); ;
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;
                options.UserInteraction.LoginUrl = "/Account/Login";
                options.UserInteraction.LogoutUrl = "/Account/Logout";
                options.Authentication = new AuthenticationOptions()
                {
                    CookieLifetime = TimeSpan.FromHours(10),
                    CookieSlidingExpiration = true
                };
            })
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
            options.EnableTokenCleanup = true;
        })
        .AddAspNetIdentity<AppUser>()
        .AddExtensionGrantValidator<PhoneNumberTokenGrantValidator>();

    if (Environment.IsDevelopment())
    {
        builder.AddDeveloperSigningCredential();
    }
    else
    {
        builder.AddSigningCredential(services.BuildServiceProvider().
            GetService<ISecretStoreService>().
            TokenSigningCertificate().Result);
    }

    //....    
}

Also:

    public interface ISecretStoreService
{
    Task<X509Certificate2> TokenValidationCertificate();
    Task<X509Certificate2> TokenSigningCertificate();
    Task<X509Certificate2> RetrieveCertificate(string certName);
    Task<X509Certificate2> RetrieveSecretAsCertificate(string certName);
}

I am getting this warning and it makes sense as service locator is an anti-pattern:

ASP0000 Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.

The question here is, how can I avoid calling services.BuildServiceProvider() inside the Startup class' ConfigureServices(IServiceCollection services){...} to follow the correct pattern and to get rid of this warning?

I went through some answers on SO but it was just to get rid of the warning but still inject the whole Service Locator which is an anti-pattern. I'd say that the only solution in mind is to extend the middleware to add a specific logic (middleware options) so I can call it without the need to construct the services.BuildServiceProvider().

Marzouk
  • 2,650
  • 3
  • 25
  • 56
  • 1
    Does this answer your question? [Calling 'BuildServiceProvider' from application code results in copy of Singleton warning. How do I avoid this?](https://stackoverflow.com/questions/58999401/calling-buildserviceprovider-from-application-code-results-in-copy-of-singleto) – Roman Marusyk Feb 27 '20 at 13:51
  • Actually no even in the question itself you will find the voted answer not that clean. I'd say that the answer was just to remove the warning but it still uses the same service locator pattern. I can say that the best way to do that is extending the middleware options and add a custom extension method that does your logic and can accept just your specific injections. – Marzouk Feb 27 '20 at 13:57
  • 1
    @Marzouk Can you include `builder` so we can get better context of the code. – Nkosi Feb 27 '20 at 14:25
  • @Nkosi Thanks, I already updated it – Marzouk Feb 27 '20 at 14:36
  • @Marzouk What is `ISecretStoreService` and its related members in the snippet – Nkosi Feb 27 '20 at 15:06
  • @Nkosi It's just an interface I am defining in my core project so I can implement it to retrieve secrets from something like AzureKeyVault for example. I updated the question with it. – Marzouk Feb 27 '20 at 15:09
  • 1
    @Marzouk understood. I am just trying to see all the parts involved to figure out if a workaround can be found. Primarily around this source code https://github.com/IdentityServer/IdentityServer4/blob/4ef0886e03d2b10acdbd2e876f521d6b636fc81d/src/IdentityServer4/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs#L76 – Nkosi Feb 27 '20 at 15:15

0 Answers0