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.
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>();