Search code examples
c#asp.net-coredependency-injectionidentityserver4

How do you use HttpClient dependency injection with IdentityServer4?


I have the following implementation of IResourceOwnerPasswordValidator:

public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
    private HttpClient HttpClient { get; set; }

    public ResourceOwnerPasswordValidator(HttpClient httpClient)
    {
        this.HttpClient = httpClient;
    }
}

I have the following code in ConfigureServices in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    var identityServerBuilder = services.AddIdentityServer();

    identityServerBuilder.Services.AddHttpClient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

    identityServerBuilder
        .AddInMemoryIdentityResources(ConfigurationData.IdentityResources)
        .AddInMemoryApiResources(ConfigurationData.ApiResources)
        .AddInMemoryClients(ConfigurationData.Clients)
        .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
        .AddProfileService<ProfileService>();

    // not recommended for production - you need to store your key material somewhere secure
    identityServerBuilder.AddDeveloperSigningCredential();
}

I have tried several different alternatives to the line registering the HttpClient without any luck:

identityServerBuilder.Services.AddHttpClient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

services.AddHttpClient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

services.AddHttpClient<ResourceOwnerPasswordValidator>();

I always get the following error:

Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate 'ResourceOwnerPasswordValidator'

If I instantiate HttpClient myself in the ResourceOwnerPasswordValidator class everything works correctly, but I've read it's bad practice to do so and I'd like to figure out why dependency injection isn't working. I've been able to use typed HttpClient dependency injection in other projects without any issues.

If it's helpful, here is the source for the IdentityServer4 extension methods.

Any assistance would be greatly appreciated.


Solution

  • identityServerBuilder.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
    

    This does the following under the hood:

    builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
    

    So this is actually equivalent to just calling directly:

    services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
    

    This call however is in conflict with what the AddHttpClient method does because that one does not just register the service directly but instead registers a factory that will resolve the HttpClient through the HttpClientFactory first.

    So basically, by calling the AddResourceOwnerValidator<>() afterwards, you are overwriting the HTTP client registration.

    It should work if you move the registrations around, but since the AddResourceOwnerValidator<>() doesn’t actually do anything special other than registering that interface type, you can just leave that one out completely:

    // register the IResourceOwnerPasswordValidator as an HTTP client
    services.AddHttpClient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
    
    // do not register anther IResourceOwnerPasswordValidator
    services.AddIdentityServer()
        .AddInMemoryIdentityResources(ConfigurationData.IdentityResources)
        .AddInMemoryApiResources(ConfigurationData.ApiResources)
        .AddInMemoryClients(ConfigurationData.Clients)
        .AddProfileService<ProfileService>();