/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.ComponentModel;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using OpenIddict.Client.SystemNetHttp;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
namespace OpenIddict.Client.WebIntegration;
///
/// Contains the methods required to ensure that the OpenIddict client Web integration configuration is valid.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed partial class OpenIddictClientWebIntegrationConfiguration : IConfigureOptions,
IConfigureNamedOptions
{
#if !SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER
private readonly IServiceProvider _provider;
///
/// Creates a new instance of the class.
///
/// The service provider.
public OpenIddictClientWebIntegrationConfiguration(IServiceProvider provider)
=> _provider = provider ?? throw new ArgumentNullException(nameof(provider));
#endif
///
public void Configure(OpenIddictClientOptions options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
// Register the built-in event handlers used by the OpenIddict client Web components.
options.Handlers.AddRange(OpenIddictClientWebIntegrationHandlers.DefaultHandlers);
}
///
public void Configure(HttpClientFactoryOptions options) => Configure(Options.DefaultName, options);
///
public void Configure(string? name, HttpClientFactoryOptions options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
// Only amend the HTTP client factory options if the instance is managed by OpenIddict
// and contains the name of a provider managed by OpenIddict.Client.WebIntegration.
var assembly = typeof(OpenIddictClientSystemNetHttpOptions).Assembly.GetName();
if (string.IsNullOrEmpty(name) || !name.StartsWith(assembly.Name!, StringComparison.Ordinal) ||
name.Length < assembly.Name!.Length + 1 || name[assembly.Name.Length] is not ':')
{
return;
}
// Note: while not enforced yet, Pro Santé Connect's specification requires sending a TLS
// client certificate when communicating with its backchannel OpenID Connect endpoints.
//
// For that, the primary HTTP handler must be altered or replaced by an instance that
// includes the client certificate set in the options in its certificate collection.
//
// For more information, see EXI PSC 24 in the annex part of
// https://www.legifrance.gouv.fr/jorf/id/JORFTEXT000045551195.
if (name.AsSpan(assembly.Name.Length + 1) is Providers.ProSantéConnect)
{
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
// Note: the client registration instance is not available here,
// so the provider options must be resolved via the DI container.
#if SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER
var options = builder.Services.GetRequiredService>().CurrentValue;
#else
var options = _provider.GetRequiredService>().CurrentValue;
#endif
// If a client certificate was specified, alter the HTTP handler.
if (options.ClientCertificate is X509Certificate certificate)
{
// If the primary HTTP handler is not an instance of HttpClientHandler, replace it by
// a new instance of HttpClientHandler as it required to attach the client certificate.
if (builder.PrimaryHandler is not HttpClientHandler handler)
{
builder.PrimaryHandler = handler = new HttpClientHandler();
}
handler.ClientCertificates.Add(certificate);
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
}
});
}
}
}