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.
///