diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 1576a47a..26391ede 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1414,6 +1414,9 @@ Alternatively, create a class implementing 'IOpenIddictClientHandler<HandlePo The post-logout redirection response was not correctly applied. To apply post-logout redirection responses, create a class implementing 'IOpenIddictClientHandler<ApplyPostLogoutRedirectionResponseContext>' and register it using 'services.AddOpenIddict().AddClient().AddEventHandler()'. + + The System.Net.Http client cannot be resolved. + The security token is missing. diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs index f8302944..5dc89d30 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs @@ -54,7 +54,7 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio // Only amend the HTTP client factory options if the instance is managed by OpenIddict. var assembly = typeof(OpenIddictClientSystemNetHttpOptions).Assembly.GetName(); - if (!string.Equals(name, assembly.Name, StringComparison.Ordinal)) + if (string.IsNullOrEmpty(name) || !name.StartsWith(assembly.Name!, StringComparison.Ordinal)) { return; } diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs index fe7a7737..e97be2d8 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs @@ -16,7 +16,9 @@ public static partial class OpenIddictClientSystemNetHttpHandlers /* * Configuration request processing: */ + CreateHttpClient.Descriptor, PrepareGetHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, @@ -36,7 +38,9 @@ public static partial class OpenIddictClientSystemNetHttpHandlers /* * Cryptography request processing: */ + CreateHttpClient.Descriptor, PrepareGetHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs index cfae0d9f..7b89c3a0 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs @@ -19,7 +19,9 @@ public static partial class OpenIddictClientSystemNetHttpHandlers /* * Token request processing: */ + CreateHttpClient.Descriptor, PreparePostHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs index 37ebe902..7d505d79 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs @@ -19,7 +19,9 @@ public static partial class OpenIddictClientSystemNetHttpHandlers /* * Userinfo request processing: */ + CreateHttpClient.Descriptor, PrepareGetHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs index d0df4fec..c5057577 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs @@ -9,7 +9,6 @@ using System.ComponentModel; using System.Diagnostics; using System.IO.Compression; using System.Net.Http.Headers; -using System.Net.Mail; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -27,6 +26,47 @@ public static partial class OpenIddictClientSystemNetHttpHandlers .AddRange(Exchange.DefaultHandlers) .AddRange(Userinfo.DefaultHandlers); + /// + /// Contains the logic responsible for creating and attaching a . + /// + public sealed class CreateHttpClient : IOpenIddictClientHandler where TContext : BaseExternalContext + { + private readonly IHttpClientFactory _factory; + + public CreateHttpClient(IHttpClientFactory factory) + => _factory = factory ?? throw new ArgumentNullException(nameof(factory)); + + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler>() + .SetOrder(int.MinValue + 100_000) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(TContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + var assembly = typeof(OpenIddictClientSystemNetHttpOptions).Assembly.GetName(); + var name = !string.IsNullOrEmpty(context.Registration.ProviderName) ? + $"{assembly.Name}:{context.Registration.ProviderName}" : assembly.Name!; + + // Create and store the HttpClient in the transaction properties. + context.Transaction.SetProperty(typeof(HttpClient).FullName!, _factory.CreateClient(name) ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0174))); + + return default; + } + } + /// /// Contains the logic responsible for preparing an HTTP GET request message. /// @@ -39,7 +79,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler>() - .SetOrder(int.MinValue + 100_000) + .SetOrder(CreateHttpClient.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -91,6 +131,53 @@ public static partial class OpenIddictClientSystemNetHttpHandlers } } + /// + /// Contains the logic responsible for attaching the HTTP version to the HTTP request message. + /// + public sealed class AttachHttpVersion : IOpenIddictClientHandler where TContext : BaseExternalContext + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler>() + .SetOrder(PreparePostHttpRequest.Descriptor.Order + 1_000) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(TContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + +#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION || SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION_POLICY + // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, + // this may indicate that the request was incorrectly processed by another client stack. + var request = context.Transaction.GetHttpRequestMessage() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); + + var client = context.Transaction.GetHttpClient() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0372)); + +#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION + // If supported, import the HTTP version from the client instance. + request.Version = client.DefaultRequestVersion; +#endif + +#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION_POLICY + // If supported, import the HTTP version policy from the client instance. + request.VersionPolicy = client.DefaultVersionPolicy; +#endif +#endif + return default; + } + } + /// /// Contains the logic responsible for attaching the appropriate HTTP /// Accept-* headers to the HTTP request message to receive JSON responses. @@ -104,7 +191,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler>() - .SetOrder(PreparePostHttpRequest.Descriptor.Order + 1_000) + .SetOrder(AttachHttpVersion.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -319,11 +406,6 @@ public static partial class OpenIddictClientSystemNetHttpHandlers /// public sealed class SendHttpRequest : IOpenIddictClientHandler where TContext : BaseExternalContext { - private readonly IHttpClientFactory _factory; - - public SendHttpRequest(IHttpClientFactory factory) - => _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - /// /// Gets the default descriptor definition assigned to this handler. /// @@ -348,19 +430,10 @@ public static partial class OpenIddictClientSystemNetHttpHandlers var request = context.Transaction.GetHttpRequestMessage() ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); - var assembly = typeof(OpenIddictClientSystemNetHttpOptions).Assembly.GetName(); - using var client = _factory.CreateClient(assembly.Name!) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0174)); + // Note: a "using" statement is deliberately used here to dispose of the client in this handler. + using var client = context.Transaction.GetHttpClient() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0372)); -#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION - // If supported, import the HTTP version from the client instance. - request.Version = client.DefaultRequestVersion; -#endif - -#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION_POLICY - // If supported, import the HTTP version policy from the client instance. - request.VersionPolicy = client.DefaultVersionPolicy; -#endif HttpResponseMessage response; try diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHelpers.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHelpers.cs index 37fcace9..66d5e0e9 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHelpers.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHelpers.cs @@ -13,6 +13,14 @@ namespace System.Net.Http; /// public static class OpenIddictClientSystemNetHttpHelpers { + /// + /// Gets the associated with the current context. + /// + /// The transaction instance. + /// The instance or if it couldn't be found. + public static HttpClient? GetHttpClient(this OpenIddictClientTransaction transaction) + => transaction.GetProperty(typeof(HttpClient).FullName!); + /// /// Gets the associated with the current context. /// diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs index c461994e..7ad104db 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs @@ -16,7 +16,9 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers /* * Configuration request processing: */ + CreateHttpClient.Descriptor, PrepareGetHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, @@ -36,7 +38,9 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers /* * Cryptography request processing: */ + CreateHttpClient.Descriptor, PrepareGetHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs index aea375ed..a7ced04b 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs @@ -19,7 +19,9 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers /* * Introspection request processing: */ + CreateHttpClient.Descriptor, PreparePostHttpRequest.Descriptor, + AttachHttpVersion.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, AttachFromHeader.Descriptor, diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs index a32ab895..26867a4a 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs @@ -9,7 +9,6 @@ using System.ComponentModel; using System.Diagnostics; using System.IO.Compression; using System.Net.Http.Headers; -using System.Net.Mail; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -26,6 +25,45 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers .AddRange(Discovery.DefaultHandlers) .AddRange(Introspection.DefaultHandlers); + /// + /// Contains the logic responsible for creating and attaching a . + /// + public sealed class CreateHttpClient : IOpenIddictValidationHandler where TContext : BaseExternalContext + { + private readonly IHttpClientFactory _factory; + + public CreateHttpClient(IHttpClientFactory factory) + => _factory = factory ?? throw new ArgumentNullException(nameof(factory)); + + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictValidationHandlerDescriptor Descriptor { get; } + = OpenIddictValidationHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler>() + .SetOrder(int.MinValue + 100_000) + .SetType(OpenIddictValidationHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(TContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + var assembly = typeof(OpenIddictValidationSystemNetHttpOptions).Assembly.GetName(); + + // Create and store the HttpClient in the transaction properties. + context.Transaction.SetProperty(typeof(HttpClient).FullName!, _factory.CreateClient(assembly.Name!) ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0174))); + + return default; + } + } + /// /// Contains the logic responsible for preparing an HTTP GET request message. /// @@ -38,7 +76,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers = OpenIddictValidationHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler>() - .SetOrder(int.MinValue + 100_000) + .SetOrder(CreateHttpClient.Descriptor.Order + 1_000) .SetType(OpenIddictValidationHandlerType.BuiltIn) .Build(); @@ -90,6 +128,53 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers } } + /// + /// Contains the logic responsible for attaching the HTTP version to the HTTP request message. + /// + public sealed class AttachHttpVersion : IOpenIddictValidationHandler where TContext : BaseExternalContext + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictValidationHandlerDescriptor Descriptor { get; } + = OpenIddictValidationHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler>() + .SetOrder(PreparePostHttpRequest.Descriptor.Order + 1_000) + .SetType(OpenIddictValidationHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(TContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + +#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION || SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION_POLICY + // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, + // this may indicate that the request was incorrectly processed by another client stack. + var request = context.Transaction.GetHttpRequestMessage() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); + + var client = context.Transaction.GetHttpClient() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0372)); + +#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION + // If supported, import the HTTP version from the client instance. + request.Version = client.DefaultRequestVersion; +#endif + +#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION_POLICY + // If supported, import the HTTP version policy from the client instance. + request.VersionPolicy = client.DefaultVersionPolicy; +#endif +#endif + return default; + } + } + /// /// Contains the logic responsible for attaching the appropriate HTTP /// Accept-* headers to the HTTP request message to receive JSON responses. @@ -103,7 +188,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers = OpenIddictValidationHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler>() - .SetOrder(PreparePostHttpRequest.Descriptor.Order + 1_000) + .SetOrder(AttachHttpVersion.Descriptor.Order + 1_000) .SetType(OpenIddictValidationHandlerType.BuiltIn) .Build(); @@ -320,11 +405,6 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers /// public sealed class SendHttpRequest : IOpenIddictValidationHandler where TContext : BaseExternalContext { - private readonly IHttpClientFactory _factory; - - public SendHttpRequest(IHttpClientFactory factory) - => _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - /// /// Gets the default descriptor definition assigned to this handler. /// @@ -349,19 +429,10 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers var request = context.Transaction.GetHttpRequestMessage() ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); - var assembly = typeof(OpenIddictValidationSystemNetHttpOptions).Assembly.GetName(); - using var client = _factory.CreateClient(assembly.Name!) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0174)); + // Note: a "using" statement is deliberately used here to dispose of the client in this handler. + using var client = context.Transaction.GetHttpClient() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0372)); -#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION - // If supported, import the HTTP version from the client instance. - request.Version = client.DefaultRequestVersion; -#endif - -#if SUPPORTS_HTTP_CLIENT_DEFAULT_REQUEST_VERSION_POLICY - // If supported, import the HTTP version policy from the client instance. - request.VersionPolicy = client.DefaultVersionPolicy; -#endif HttpResponseMessage response; try diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHelpers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHelpers.cs index 9f2bc2e3..05479e54 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHelpers.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHelpers.cs @@ -13,6 +13,14 @@ namespace System.Net.Http; /// public static class OpenIddictValidationSystemNetHttpHelpers { + /// + /// Gets the associated with the current context. + /// + /// The transaction instance. + /// The instance or if it couldn't be found. + public static HttpClient? GetHttpClient(this OpenIddictValidationTransaction transaction) + => transaction.GetProperty(typeof(HttpClient).FullName!); + /// /// Gets the associated with the current context. ///