System.Net.Http integration clientvalidation
To be able to communicate with HTTP servers, the OpenIddict client and validation stacks rely on companion packages named OpenIddict.Client.SystemNetHttp and OpenIddict.Validation.SystemNetHttp.
INFO
These packages are responsible for instantiating the HttpClient objects needed to send HTTP requests (using Microsoft.Extensions.Http) and managing their lifetime. They also take care of preparing the HttpRequestMessage instances and extracting the corresponding HttpResponseMessage once the HTTP response has been received.
Basic configuration clientvalidation
To configure the System.Net.Http integration, you'll need to:
- Reference the
OpenIddict.Client.SystemNetHttpand/orOpenIddict.Validation.SystemNetHttppackages (depending on whether you need the client and/or validation features in your project):
<PackageReference Include="OpenIddict.Client.SystemNetHttp" Version="6.2.0" />
<PackageReference Include="OpenIddict.Validation.SystemNetHttp" Version="6.2.0" />- Call
UseSystemNetHttp()for each OpenIddict feature (client and validation) you want to add:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp();
})
.AddValidation(options =>
{
options.UseSystemNetHttp();
});Advanced configuration
Configure a contact address clientvalidation
The System.Net.Http integration allows setting an email address that is sent as part of the standard From HTTP header. While not strictly required, doing that can help authorization servers managed by third parties contact you when they think something is wrong with your use of their service:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.SetContactAddress("contact@contoso.com");
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.SetContactAddress("contact@contoso.com");
});Configure a product name clientvalidation
While OpenIddict always sends a default User-Agent (containing the name of the System.Net.Http integration and its .NET assembly version), it is recommended to set a product name and a product version to help OpenIddict send a more detailed User-Agent header:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.SetProductInformation("Contoso", "1.0.0");
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.SetProductInformation("Contoso", "1.0.0");
});The identity of a specific .NET Asssembly can also be used:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.SetProductInformation(typeof(Program).Assembly);
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.SetProductInformation(typeof(Program).Assembly);
});Configure a custom HTTP error policy clientvalidation
To mitigate transient HTTP errors (e.g temporary connectivity issues), OpenIddict uses a default error policy that automatically retries sending a failed HTTP request up to 4 times. While the default policy is appropriate for most applications, the default policy can be overridden using the SetHttpErrorPolicy() API:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.SetHttpErrorPolicy(HttpPolicyExtensions.HandleTransientHttpError()
.OrResult(static response => response.StatusCode is HttpStatusCode.NotFound)
.WaitAndRetryAsync(4, static attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))));
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.SetHttpErrorPolicy(HttpPolicyExtensions.HandleTransientHttpError()
.OrResult(static response => response.StatusCode is HttpStatusCode.NotFound)
.WaitAndRetryAsync(4, static attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))));
});WARNING
On .NET 8.0 and higher, OpenIddict no longer registers an IAsyncPolicy<HttpResponseMessage> by default and uses a ResiliencePipeline<HttpResponseMessage> instead. If you decide to explicitly configure an HTTP error policy, the HTTP resilience pipeline will be ignored, whether you use the default instance or a custom resilience pipeline.
Configure a custom HTTP resilience pipeline clientvalidation
On .NET 8.0 and higher, OpenIddict uses a ResiliencePipeline<HttpResponseMessage> instead of an IAsyncPolicy<HttpResponseMessage> to deal with transient HTTP errors (failed HTTP requests are retried up to 4 times). While the default resilience pipeline is appropriate for most applications, the default resilience pipeline can be overridden using the SetHttpResiliencePipeline() API:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.SetHttpResiliencePipeline(builder => builder.AddRetry(new HttpRetryStrategyOptions
{
DelayGenerator = static arguments => new(
TimeSpan.FromSeconds(Math.Pow(2, arguments.AttemptNumber))),
MaxRetryAttempts = 4,
ShouldHandle = static arguments => new(
HttpClientResiliencePredicates.IsTransient(arguments.Outcome) ||
arguments.Outcome.Result?.StatusCode is HttpStatusCode.NotFound)
}));
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.SetHttpResiliencePipeline(builder => builder.AddRetry(new HttpRetryStrategyOptions
{
DelayGenerator = static arguments => new(
TimeSpan.FromSeconds(Math.Pow(2, arguments.AttemptNumber))),
MaxRetryAttempts = 4,
ShouldHandle = static arguments => new(
HttpClientResiliencePredicates.IsTransient(arguments.Outcome) ||
arguments.Outcome.Result?.StatusCode is HttpStatusCode.NotFound)
}));
});Register a custom HttpClient configuration delegate clientvalidation
For scenarios that require tweaking the HttpClient instances used by OpenIddict (e.g to add a custom static header), an Action<HttpClient> configuration delegate can be registered using the ConfigureHttpClient() API:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClient(client => client.DefaultRequestHeaders.Add("Custom-Header", "value"));
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClient(client => client.DefaultRequestHeaders.Add("Custom-Header", "value"));
});The OpenIddict client integration also allows configuring a provider-specific delegate:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClient(Providers.GitHub, client => client.DefaultRequestHeaders.Add("Custom-Header", "value"));
});For advanced scenarios, an Action<OpenIddictClientRegistration, HttpClient> delegate can be used instead:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClient((registration, client) =>
{
if (registration.RegistrationId is "447fbedf-dcec-47b0-9355-6e199e8f2576")
{
client.DefaultRequestHeaders.Add("Custom-Header", "value");
}
});
});Register a custom HttpClientHandler configuration delegate clientvalidation
For scenarios that require tweaking the HttpClientHandler instances used by OpenIddict (e.g to override or disable the TLS server certificate validation logic during development), an Action<HttpClientHandler> configuration delegate can be registered using the ConfigureHttpClientHandler() API:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClientHandler(handler => handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator);
})
.AddValidation(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClientHandler(handler => handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator);
});The OpenIddict client integration also allows configuring a provider-specific delegate:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClientHandler(Providers.GitHub, handler => handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator);
});For advanced scenarios, an Action<OpenIddictClientRegistration, HttpClientHandler> delegate can be used instead:
services.AddOpenIddict()
.AddClient(options =>
{
options.UseSystemNetHttp()
.ConfigureHttpClientHandler((registration, handler) =>
{
if (registration.RegistrationId is "447fbedf-dcec-47b0-9355-6e199e8f2576")
{
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
});
});