diff --git a/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs b/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs
index f66b2a49..c65ff544 100644
--- a/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs
+++ b/sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs
@@ -212,6 +212,7 @@ public class AuthorizationController : Controller
var session = sessions.LastOrDefault() ?? await _sessionManager.CreateAsync(new()
{
ApplicationId = await _applicationManager.GetIdAsync(application),
+ LoginId = result.Identity.GetClaim("login_id"),
Subject = user.Id
});
@@ -361,6 +362,7 @@ public class AuthorizationController : Controller
var session = sessions.LastOrDefault() ?? await _sessionManager.CreateAsync(new()
{
ApplicationId = await _applicationManager.GetIdAsync(application),
+ LoginId = result.Identity.GetClaim("login_id"),
Subject = user.Id
});
diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx
index b0cc79ed..02703dba 100644
--- a/src/OpenIddict.Abstractions/OpenIddictResources.resx
+++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx
@@ -501,10 +501,6 @@ This may indicate that the event handler responsible for processing OpenID Conne
No service provider was found in the OWIN context.
For the OpenIddict server services to work correctly, a per-request 'IServiceProvider' must be attached to the OWIN environment with the dictionary key 'System.IServiceProvider'.
Note: when using a dependency injection container supporting middleware resolution (like Autofac), the 'app.UseOpenIddictServer()' extension MUST NOT be called.
-
-
- The authentication handler used by the OpenIddict server components cannot be resolved from the DI container.
-To register the OWIN integration, use 'services.AddOpenIddict().AddServer().UseOwin()'.Audiences cannot be null or empty.
@@ -693,10 +689,6 @@ Make sure that neither DefaultSignInScheme nor DefaultSignOutScheme point to an
No service provider was found in the OWIN context.
For the OpenIddict validation services to work correctly, a per-request 'IServiceProvider' must be attached to the OWIN environment with the dictionary key 'System.IServiceProvider'.
Note: when using a dependency injection container supporting middleware resolution (like Autofac), the 'app.UseOpenIddictValidation()' extension MUST NOT be called.
-
-
- The authentication handler used by the OpenIddict validation components cannot be resolved from the DI container.
-To register the OWIN integration, use 'services.AddOpenIddict().AddValidation().UseOwin()'.The local server integration can only be used with direct validation.
@@ -1044,10 +1036,6 @@ Reference the 'OpenIddict.Client.SystemNetHttp' package and call 'services.AddOp
No service provider was found in the OWIN context.
For the OpenIddict client services to work correctly, a per-request 'IServiceProvider' must be attached to the OWIN environment with the dictionary key 'System.IServiceProvider'.
Note: when using a dependency injection container supporting middleware resolution (like Autofac), the 'app.UseOpenIddictClient()' extension MUST NOT be called.
-
-
- The authentication handler used by the OpenIddict client components cannot be resolved from the DI container.
-To register the OWIN integration, use 'services.AddOpenIddict().AddClient().UseOwin()'.The core services must be registered when enabling the OpenIddict client feature.
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
index 0d0ae2bc..d38513c2 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
@@ -175,15 +175,10 @@ public readonly struct OpenIddictParameter : IEquatable
null => null
};
- ///
- /// Determines whether the current
- /// instance is equal to the specified .
- ///
- /// The other object to which to compare this instance.
- ///
- /// if the two instances have both the same representation
- /// (e.g ) and value, otherwise.
- ///
+ ///
+ ///
+ /// Two instances are considered equal if they have the same representation.
+ ///
public bool Equals(OpenIddictParameter other)
{
return (_value, other._value) switch
@@ -282,22 +277,14 @@ public readonly struct OpenIddictParameter : IEquatable
};
}
- ///
- /// Determines whether the current
- /// instance is equal to the specified .
- ///
- /// The other object to which to compare this instance.
- ///
- /// if the two instances have both the same representation
- /// (e.g ) and value, otherwise.
- ///
+ ///
+ ///
+ /// Two instances are considered equal if they have the same representation.
+ ///
public override bool Equals([NotNullWhen(true)] object? obj)
=> obj is OpenIddictParameter parameter && Equals(parameter);
- ///
- /// Returns the hash code of the current instance.
- ///
- /// The hash code for the current instance.
+ ///
public override int GetHashCode()
{
return _value switch
@@ -1010,7 +997,7 @@ public readonly struct OpenIddictParameter : IEquatable
long value => value.ToString(CultureInfo.InvariantCulture),
// When the parameter is a JSON boolean value, use its string representation.
- JsonElement { ValueKind: JsonValueKind.True } => "true",
+ JsonElement { ValueKind: JsonValueKind.True } => "true",
JsonElement { ValueKind: JsonValueKind.False } => "false",
// When the parameter is a JsonElement, try to convert it if it's of a supported type.
diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreFeature.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreFeature.cs
index e9667112..ed8da712 100644
--- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreFeature.cs
+++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreFeature.cs
@@ -9,14 +9,17 @@ using System.ComponentModel;
namespace OpenIddict.Client.AspNetCore;
///
-/// Exposes the current client transaction to the ASP.NET Core host.
+/// Exposes the current client transaction to the ASP.NET Core application.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictClientAspNetCoreFeature
{
///
- /// Gets or sets the client transaction that encapsulates all specific
- /// information about an individual OpenID Connect client request.
+ /// Gets the transaction that encapsulates all specific information about an individual operation.
///
- public OpenIddictClientTransaction? Transaction { get; set; }
+ public required OpenIddictClientTransaction Transaction
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
}
diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs
index 87645a20..0c127b92 100644
--- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs
+++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs
@@ -8,6 +8,7 @@ using System.ComponentModel;
using System.Globalization;
using System.Security.Claims;
using System.Text.Encodings.Web;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static OpenIddict.Client.AspNetCore.OpenIddictClientAspNetCoreConstants;
@@ -24,22 +25,17 @@ public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler
/// Creates a new instance of the class.
///
public OpenIddictClientAspNetCoreHandler(
IOpenIddictClientDispatcher dispatcher,
- IOpenIddictClientFactory factory,
IOptionsMonitor options,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
- {
- _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
- _factory = factory ?? throw new ArgumentNullException(nameof(factory));
- }
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
public async Task HandleRequestAsync()
@@ -59,9 +55,16 @@ public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler()?.Transaction;
if (transaction is null)
{
+ var options = Context.RequestServices.GetRequiredService>();
+
// Create a new transaction and attach the HTTP request to make it available to the ASP.NET Core handlers.
- transaction = await _factory.CreateTransactionAsync(source.Token);
- transaction.Properties[typeof(HttpRequest).FullName!] = new WeakReference(Request);
+ transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = source.Token,
+ Options = options.CurrentValue,
+ Properties = { [typeof(HttpRequest).FullName!] = Request },
+ ServiceProvider = Context.RequestServices
+ };
// Attach the OpenIddict client transaction to the ASP.NET Core features
// so that it can retrieved while performing sign-in/sign-out operations.
diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHelpers.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHelpers.cs
index 1806322d..54566964 100644
--- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHelpers.cs
+++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHelpers.cs
@@ -23,17 +23,8 @@ public static class OpenIddictClientAspNetCoreHelpers
{
ArgumentNullException.ThrowIfNull(transaction);
- if (!transaction.Properties.TryGetValue(typeof(HttpRequest).FullName!, out object? property))
- {
- return null;
- }
-
- if (property is WeakReference reference && reference.TryGetTarget(out HttpRequest? request))
- {
- return request;
- }
-
- return null;
+ return transaction.Properties.TryGetValue(typeof(HttpRequest).FullName!, out object? property)
+ && property is HttpRequest request ? request : null;
}
///
diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs
index 4229683a..c05e2f88 100644
--- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs
+++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinExtensions.cs
@@ -31,7 +31,6 @@ public static class OpenIddictClientOwinExtensions
// Note: unlike regular OWIN middleware, the OpenIddict client middleware is registered
// as a scoped service in the DI container. This allows containers that support middleware
// resolution (like Autofac) to use it without requiring additional configuration.
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
// Register the built-in event handlers used by the OpenIddict OWIN client components.
diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs
index 774da6c3..0beca3c2 100644
--- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs
+++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs
@@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Claims;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Owin.Security.Infrastructure;
using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants;
@@ -23,29 +24,20 @@ namespace OpenIddict.Client.Owin;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictClientOwinHandler : AuthenticationHandler
{
- private readonly IOpenIddictClientDispatcher _dispatcher;
- private readonly IOpenIddictClientFactory _factory;
- private readonly IOptionsMonitor _options;
+ private readonly IServiceProvider _provider;
///
/// Creates a new instance of the class.
///
- /// The OpenIddict client dispatcher used by this instance.
- /// The OpenIddict client factory used by this instance.
- /// The OpenIddict client OWIN options.
- public OpenIddictClientOwinHandler(
- IOpenIddictClientDispatcher dispatcher,
- IOpenIddictClientFactory factory,
- IOptionsMonitor options)
- {
- _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
- _factory = factory ?? throw new ArgumentNullException(nameof(factory));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- }
+ /// The service provider.
+ public OpenIddictClientOwinHandler(IServiceProvider provider)
+ => _provider = provider ?? throw new ArgumentNullException(nameof(provider));
///
protected override async Task InitializeCoreAsync()
{
+ var dispatcher = _provider.GetRequiredService();
+
// Note: to ensure internal operations are not immediately cancelled when the request is aborted
// (which may represent a security risk if sensitive operations are in progress), an ad-hoc token
// source is always created and configured to be triggered 5 seconds after the request is aborted.
@@ -61,9 +53,16 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler(typeof(OpenIddictClientTransaction).FullName);
if (transaction is null)
{
+ var options = _provider.GetRequiredService>();
+
// Create a new transaction and attach the OWIN request to make it available to the OWIN handlers.
- transaction = await _factory.CreateTransactionAsync(source.Token);
- transaction.Properties[typeof(IOwinRequest).FullName!] = new WeakReference(Request);
+ transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = source.Token,
+ Options = options.CurrentValue,
+ Properties = { [typeof(IOwinRequest).FullName!] = Request },
+ ServiceProvider = _provider
+ };
// Attach the OpenIddict client transaction to the OWIN shared dictionary
// so that it can retrieved while performing sign-in/sign-out operations.
@@ -71,7 +70,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler();
+
var transaction = Context.Get(typeof(OpenIddictClientTransaction).FullName)
?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0315));
@@ -110,7 +111,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler
protected override async Task AuthenticateCoreAsync()
{
+ var dispatcher = _provider.GetRequiredService();
+
var transaction = Context.Get(typeof(OpenIddictClientTransaction).FullName)
?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0315));
@@ -140,7 +143,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler(typeof(ProcessAuthenticationContext).FullName!);
if (context is null)
{
- await _dispatcher.DispatchAsync(context = new ProcessAuthenticationContext(transaction));
+ await dispatcher.DispatchAsync(context = new ProcessAuthenticationContext(transaction));
// Store the context object in the transaction so it can be later retrieved by handlers
// that want to access the authentication result without triggering a new authentication flow.
@@ -287,7 +290,9 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler();
+ var options = _provider.GetRequiredService>();
+ var descriptions = options.CurrentValue.ForwardedAuthenticationTypes;
// Note: unlike the ASP.NET Core host, the OWIN host MUST check whether the status code
// corresponds to a challenge response, as LookupChallenge() will always return a non-null
@@ -308,7 +313,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler reference && reference.TryGetTarget(out IOwinRequest? request))
- {
- return request;
- }
-
- return null;
+ return transaction.Properties.TryGetValue(typeof(IOwinRequest).FullName!, out object? property)
+ && property is IOwinRequest request ? request : null;
}
///
diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs
index f4ac9216..3ac11be6 100644
--- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs
+++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinMiddleware.cs
@@ -28,10 +28,12 @@ using AuthenticateDelegate = Func<
///
/// Provides the entry point necessary to register the OpenIddict client handler in an OWIN pipeline.
+///
+///
/// Note: this middleware is intended to be used with dependency injection containers
/// that support middleware resolution, like Autofac. Since it depends on scoped services,
/// it is NOT recommended to instantiate it as a singleton like a regular OWIN middleware.
-///
+///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware
{
@@ -54,8 +56,7 @@ public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware>()
- ?.CurrentValue ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0316));
+ var options = _provider.GetRequiredService>().CurrentValue;
// Retrieve the existing authentication delegate.
var function = context.Get("security.Authenticate");
@@ -154,8 +155,7 @@ public sealed class OpenIddictClientOwinMiddleware : AuthenticationMiddleware
/// A new instance of the class.
protected override AuthenticationHandler CreateHandler()
- => _provider.GetService()
- ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0317));
+ => new OpenIddictClientOwinHandler(_provider);
///
/// Provides the options used by the class.
diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs
index ff628085..9698e750 100644
--- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs
+++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs
@@ -13,7 +13,6 @@ using Microsoft.Extensions.Options;
#if ANDROID
using Android.Content;
-using OpenIddict.Extensions;
#endif
namespace OpenIddict.Client.SystemIntegration;
@@ -143,12 +142,17 @@ public sealed class OpenIddictClientSystemIntegrationService
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
+ var options = scope.ServiceProvider.GetRequiredService>();
// Create a client transaction and store the specified instance so
// it can be retrieved by the event handlers that need to access it.
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
- transaction.SetProperty(typeof(TProperty).FullName!, property);
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ Properties = { [typeof(TProperty).FullName!] = property },
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessRequestContext(transaction);
await dispatcher.DispatchAsync(context);
diff --git a/src/OpenIddict.Client/IOpenIddictClientFactory.cs b/src/OpenIddict.Client/IOpenIddictClientFactory.cs
deleted file mode 100644
index 91713f97..00000000
--- a/src/OpenIddict.Client/IOpenIddictClientFactory.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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;
-
-namespace OpenIddict.Client;
-
-///
-/// Represents a service responsible for creating transactions.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public interface IOpenIddictClientFactory
-{
- ///
- /// Creates a new that is used as a
- /// way to store per-request data needed to process the requested operation.
- ///
- /// The that can be used to abort the operation.
- ///
- /// Note: the specified is automatically attached to the returned transaction.
- ///
- ///
- /// A that can be used to monitor the asynchronous
- /// operation, whose result returns the created transaction.
- ///
- ValueTask CreateTransactionAsync(CancellationToken cancellationToken);
-}
diff --git a/src/OpenIddict.Client/OpenIddictClientDispatcher.cs b/src/OpenIddict.Client/OpenIddictClientDispatcher.cs
index 5e7dae63..77a430c5 100644
--- a/src/OpenIddict.Client/OpenIddictClientDispatcher.cs
+++ b/src/OpenIddict.Client/OpenIddictClientDispatcher.cs
@@ -6,7 +6,6 @@
using System.ComponentModel;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
namespace OpenIddict.Client;
@@ -16,113 +15,73 @@ namespace OpenIddict.Client;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictClientDispatcher : IOpenIddictClientDispatcher
{
- private readonly ILogger _logger;
- private readonly IOptionsMonitor _options;
- private readonly IServiceProvider _provider;
-
- ///
- /// Creates a new instance of the class.
- ///
- public OpenIddictClientDispatcher(
- ILogger logger,
- IOptionsMonitor options,
- IServiceProvider provider)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- _provider = provider ?? throw new ArgumentNullException(nameof(provider));
- }
-
///
public async ValueTask DispatchAsync(TContext context) where TContext : BaseContext
{
ArgumentNullException.ThrowIfNull(context);
- await foreach (var handler in GetHandlersAsync())
+ // Note: the descriptors collection is sorted during options initialization for performance reasons.
+ foreach (var descriptor in context.Options.Handlers)
{
context.CancellationToken.ThrowIfCancellationRequested();
+ if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor))
+ {
+ continue;
+ }
+
+ var handler = descriptor.ServiceDescriptor.ImplementationInstance as IOpenIddictClientHandler
+ ?? context.ServiceProvider.GetService(descriptor.ServiceDescriptor.ServiceType) as IOpenIddictClientHandler
+ ?? throw new InvalidOperationException(SR.FormatID0098(descriptor.ServiceDescriptor.ServiceType));
+
try
{
await handler.HandleAsync(context);
}
- catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception) && _logger.IsEnabled(LogLevel.Debug))
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception) && context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6132, exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName);
+ context.Logger.LogDebug(6132, exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName);
throw;
}
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6133, SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6133, SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName);
}
switch (context)
{
case BaseRequestContext { IsRequestHandled: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6134, SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6134, SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
case BaseRequestContext { IsRequestSkipped: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6135, SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6135, SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
case BaseValidatingContext { IsRejected: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6136, SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6136, SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
-
- default: continue;
- }
- }
-
- async IAsyncEnumerable> GetHandlersAsync()
- {
- // Note: the descriptors collection is sorted during options initialization for performance reasons.
- var descriptors = _options.CurrentValue.Handlers;
- if (descriptors.Count is 0)
- {
- yield break;
- }
-
- for (var index = 0; index < descriptors.Count; index++)
- {
- var descriptor = descriptors[index];
- if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor))
- {
- continue;
- }
-
- yield return descriptor.ServiceDescriptor switch
- {
- { ImplementationInstance: IOpenIddictClientHandler handler } => handler,
-
- _ when _provider.GetService(descriptor.ServiceDescriptor.ServiceType)
- is IOpenIddictClientHandler handler => handler,
-
- _ => throw new InvalidOperationException(SR.FormatID0312(descriptor.ServiceDescriptor.ServiceType))
- };
}
}
async ValueTask IsActiveAsync(OpenIddictClientHandlerDescriptor descriptor)
{
- for (var index = 0; index < descriptor.FilterTypes.Length; index++)
+ foreach (var type in descriptor.FilterTypes)
{
- if (_provider.GetService(descriptor.FilterTypes[index]) is not IOpenIddictClientHandlerFilter filter)
- {
- throw new InvalidOperationException(SR.FormatID0099(descriptor.FilterTypes[index]));
- }
+ var filter = context.ServiceProvider.GetService(type) as IOpenIddictClientHandlerFilter
+ ?? throw new InvalidOperationException(SR.FormatID0099(type));
if (!await filter.IsActiveAsync(context))
{
diff --git a/src/OpenIddict.Client/OpenIddictClientEvents.cs b/src/OpenIddict.Client/OpenIddictClientEvents.cs
index 4f11e13f..c9288b3f 100644
--- a/src/OpenIddict.Client/OpenIddictClientEvents.cs
+++ b/src/OpenIddict.Client/OpenIddictClientEvents.cs
@@ -8,6 +8,7 @@ using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Client;
@@ -66,7 +67,8 @@ public static partial class OpenIddictClientEvents
///
/// Gets the logger responsible for logging processed operations.
///
- public ILogger Logger => Transaction.Logger;
+ public ILogger Logger
+ => field ??= Transaction.ServiceProvider.GetRequiredService>();
///
/// Gets the OpenIddict client options.
@@ -90,6 +92,12 @@ public static partial class OpenIddictClientEvents
get => Transaction.Registration;
set => Transaction.Registration = value;
}
+
+ ///
+ /// Gets the service provider associated with the current transaction.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public IServiceProvider ServiceProvider => Transaction.ServiceProvider;
}
///
diff --git a/src/OpenIddict.Client/OpenIddictClientExtensions.cs b/src/OpenIddict.Client/OpenIddictClientExtensions.cs
index 8b1b726f..f6d0ef48 100644
--- a/src/OpenIddict.Client/OpenIddictClientExtensions.cs
+++ b/src/OpenIddict.Client/OpenIddictClientExtensions.cs
@@ -28,8 +28,7 @@ public static class OpenIddictClientExtensions
builder.Services.AddLogging();
builder.Services.AddOptions();
- builder.Services.TryAddScoped();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
// Register the built-in filters used by the default OpenIddict client event handlers.
diff --git a/src/OpenIddict.Client/OpenIddictClientFactory.cs b/src/OpenIddict.Client/OpenIddictClientFactory.cs
deleted file mode 100644
index 1ae7a02f..00000000
--- a/src/OpenIddict.Client/OpenIddictClientFactory.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace OpenIddict.Client;
-
-///
-/// Represents a service responsible for creating transactions.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public sealed class OpenIddictClientFactory : IOpenIddictClientFactory
-{
- private readonly ILogger _logger;
- private readonly IOptionsMonitor _options;
-
- ///
- /// Creates a new instance of the class.
- ///
- public OpenIddictClientFactory(
- ILogger logger,
- IOptionsMonitor options)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- }
-
- ///
- public ValueTask CreateTransactionAsync(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new(Task.FromCanceled(cancellationToken));
- }
-
- return new(new OpenIddictClientTransaction
- {
- CancellationToken = cancellationToken,
- Logger = _logger,
- Options = _options.CurrentValue
- });
- }
-}
diff --git a/src/OpenIddict.Client/OpenIddictClientService.cs b/src/OpenIddict.Client/OpenIddictClientService.cs
index da9a3507..3d30b5b9 100644
--- a/src/OpenIddict.Client/OpenIddictClientService.cs
+++ b/src/OpenIddict.Client/OpenIddictClientService.cs
@@ -258,15 +258,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
+ var options = scope.ServiceProvider.GetRequiredService>();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -328,15 +330,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
+ var options = scope.ServiceProvider.GetRequiredService>();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessChallengeContext(transaction)
{
@@ -410,14 +414,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -500,14 +507,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -592,16 +602,17 @@ public class OpenIddictClientService
try
{
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
var options = scope.ServiceProvider.GetRequiredService>();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -704,15 +715,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
+ var options = scope.ServiceProvider.GetRequiredService>();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessChallengeContext(transaction)
{
@@ -783,14 +796,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -870,14 +886,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -957,14 +976,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -1042,14 +1064,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessIntrospectionContext(transaction)
{
@@ -1101,14 +1126,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessRevocationContext(transaction)
{
@@ -1168,14 +1196,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var request = new OpenIddictRequest();
request = await PrepareConfigurationRequestAsync();
@@ -1288,15 +1319,17 @@ public class OpenIddictClientService
request.CancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
+ var options = scope.ServiceProvider.GetRequiredService>();
- var transaction = await factory.CreateTransactionAsync(request.CancellationToken);
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = request.CancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var context = new ProcessSignOutContext(transaction)
{
@@ -1359,14 +1392,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
var request = new OpenIddictRequest();
request = await PrepareJsonWebKeySetRequestAsync();
@@ -1497,14 +1533,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
request = await PrepareDeviceAuthorizationRequestAsync();
request = await ApplyDeviceAuthorizationRequestAsync();
@@ -1638,14 +1677,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
request = await PrepareIntrospectionRequestAsync();
request = await ApplyIntrospectionRequestAsync();
@@ -1782,14 +1824,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
request = await PreparePushedAuthorizationRequestAsync();
request = await ApplyPushedAuthorizationRequestAsync();
@@ -1923,14 +1968,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
request = await PrepareRevocationRequestAsync();
request = await ApplyRevocationRequestAsync();
@@ -2065,14 +2113,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
request = await PrepareTokenRequestAsync();
request = await ApplyTokenRequestAsync();
@@ -2208,14 +2259,17 @@ public class OpenIddictClientService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictClientTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = scope.ServiceProvider
+ };
request = await PrepareUserInfoRequestAsync();
request = await ApplyUserInfoRequestAsync();
diff --git a/src/OpenIddict.Client/OpenIddictClientTransaction.cs b/src/OpenIddict.Client/OpenIddictClientTransaction.cs
index 88a46d20..9935640e 100644
--- a/src/OpenIddict.Client/OpenIddictClientTransaction.cs
+++ b/src/OpenIddict.Client/OpenIddictClientTransaction.cs
@@ -5,23 +5,22 @@
*/
using System.ComponentModel;
-using Microsoft.Extensions.Logging;
namespace OpenIddict.Client;
///
-/// Represents the context associated with an OpenID Connect client request.
+/// Represents the context associated with an OpenID Connect client operation.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictClientTransaction
{
///
- /// Gets or sets the cancellation token used to determine if the operation was aborted.
+ /// Gets the cancellation token used to determine if the operation was aborted.
///
- public CancellationToken CancellationToken { get; set; }
+ public required CancellationToken CancellationToken { get; init; }
///
- /// Gets or sets the type of the endpoint processing the current request.
+ /// Gets or sets the type of the endpoint processing the current transaction.
///
public OpenIddictClientEndpointType EndpointType { get; set; }
@@ -36,29 +35,36 @@ public sealed class OpenIddictClientTransaction
public Uri? BaseUri { get; set; }
///
- /// Gets or sets the logger associated with the current request.
+ /// Gets the options associated with the current transaction.
///
- public ILogger Logger { get; set; } = default!;
+ public required OpenIddictClientOptions Options
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
///
- /// Gets or sets the options associated with the current request.
- ///
- public OpenIddictClientOptions Options { get; set; } = default!;
-
- ///
- /// Gets the additional properties associated with the current request.
+ /// Gets the additional properties associated with the current transaction.
///
public Dictionary Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
///
- /// Gets or sets the client registration used for the current request.
+ /// Gets or sets the client registration used for the current transaction.
///
- public OpenIddictClientRegistration Registration { get; set; } = default!;
+ public OpenIddictClientRegistration Registration
+ {
+ get;
+ set { ArgumentNullException.ThrowIfNull(value); field = value; }
+ } = default!;
///
- /// Gets or sets the server configuration used for the current request.
+ /// Gets or sets the server configuration used for the current transaction.
///
- public OpenIddictConfiguration Configuration { get; set; } = default!;
+ public OpenIddictConfiguration Configuration
+ {
+ get;
+ set { ArgumentNullException.ThrowIfNull(value); field = value; }
+ } = default!;
///
/// Gets or sets the current OpenID Connect request.
@@ -69,4 +75,13 @@ public sealed class OpenIddictClientTransaction
/// Gets or sets the current OpenID Connect response being returned.
///
public OpenIddictResponse? Response { get; set; }
+
+ ///
+ /// Gets the service provider used to resolve services.
+ ///
+ public required IServiceProvider ServiceProvider
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
}
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreFeature.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreFeature.cs
index 47a62032..e1d5be65 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreFeature.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreFeature.cs
@@ -9,14 +9,17 @@ using System.ComponentModel;
namespace OpenIddict.Server.AspNetCore;
///
-/// Exposes the current server transaction to the ASP.NET Core host.
+/// Exposes the current server transaction to the ASP.NET Core application.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictServerAspNetCoreFeature
{
///
- /// Gets or sets the server transaction that encapsulates all specific
- /// information about an individual OpenID Connect server request.
+ /// Gets the transaction that encapsulates all specific information about an individual operation.
///
- public OpenIddictServerTransaction? Transaction { get; set; }
+ public required OpenIddictServerTransaction Transaction
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
}
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs
index 35d69def..410fa02b 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs
@@ -7,6 +7,7 @@
using System.ComponentModel;
using System.Security.Claims;
using System.Text.Encodings.Web;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreConstants;
@@ -24,22 +25,17 @@ public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler
/// Creates a new instance of the class.
///
public OpenIddictServerAspNetCoreHandler(
IOpenIddictServerDispatcher dispatcher,
- IOpenIddictServerFactory factory,
IOptionsMonitor options,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
- {
- _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
- _factory = factory ?? throw new ArgumentNullException(nameof(factory));
- }
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
public async Task HandleRequestAsync()
@@ -59,9 +55,16 @@ public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler()?.Transaction;
if (transaction is null)
{
+ var options = Context.RequestServices.GetRequiredService>();
+
// Create a new transaction and attach the HTTP request to make it available to the ASP.NET Core handlers.
- transaction = await _factory.CreateTransactionAsync(source.Token);
- transaction.Properties[typeof(HttpRequest).FullName!] = new WeakReference(Request);
+ transaction = new OpenIddictServerTransaction
+ {
+ CancellationToken = source.Token,
+ Options = options.CurrentValue,
+ Properties = { [typeof(HttpRequest).FullName!] = Request },
+ ServiceProvider = Context.RequestServices
+ };
// Attach the OpenIddict server transaction to the ASP.NET Core features
// so that it can retrieved while performing sign-in/sign-out operations.
diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHelpers.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHelpers.cs
index b441d1ce..85ef068f 100644
--- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHelpers.cs
+++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHelpers.cs
@@ -23,17 +23,9 @@ public static class OpenIddictServerAspNetCoreHelpers
{
ArgumentNullException.ThrowIfNull(transaction);
- if (!transaction.Properties.TryGetValue(typeof(HttpRequest).FullName!, out object? property))
- {
- return null;
- }
+ return transaction.Properties.TryGetValue(typeof(HttpRequest).FullName!, out object? property)
+ && property is HttpRequest request ? request : null;
- if (property is WeakReference reference && reference.TryGetTarget(out HttpRequest? request))
- {
- return request;
- }
-
- return null;
}
///
@@ -52,7 +44,7 @@ public static class OpenIddictServerAspNetCoreHelpers
/// Retrieves the instance stored in .
///
/// The context instance.
- /// The instance or null if it couldn't be found.
+ /// The instance or if it couldn't be found.
public static OpenIddictRequest? GetOpenIddictServerRequest(this HttpContext context)
{
ArgumentNullException.ThrowIfNull(context);
@@ -64,7 +56,7 @@ public static class OpenIddictServerAspNetCoreHelpers
/// Retrieves the instance stored in .
///
/// The context instance.
- /// The instance or null if it couldn't be found.
+ /// The instance or if it couldn't be found.
public static OpenIddictResponse? GetOpenIddictServerResponse(this HttpContext context)
{
ArgumentNullException.ThrowIfNull(context);
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs
index 98ee11c6..e5c65cde 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinExtensions.cs
@@ -31,7 +31,6 @@ public static class OpenIddictServerOwinExtensions
// Note: unlike regular OWIN middleware, the OpenIddict server middleware is registered
// as a scoped service in the DI container. This allows containers that support middleware
// resolution (like Autofac) to use it without requiring additional configuration.
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
// Register the built-in event handlers used by the OpenIddict OWIN server components.
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs
index 93dce5a9..68b8d3c8 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs
@@ -6,6 +6,8 @@
using System.ComponentModel;
using System.Security.Claims;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Microsoft.Owin.Security.Infrastructure;
using static OpenIddict.Server.Owin.OpenIddictServerOwinConstants;
using Properties = OpenIddict.Server.Owin.OpenIddictServerOwinConstants.Properties;
@@ -18,25 +20,20 @@ namespace OpenIddict.Server.Owin;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictServerOwinHandler : AuthenticationHandler
{
- private readonly IOpenIddictServerDispatcher _dispatcher;
- private readonly IOpenIddictServerFactory _factory;
+ private readonly IServiceProvider _provider;
///
/// Creates a new instance of the class.
///
- /// The OpenIddict server dispatcher used by this instance.
- /// The OpenIddict server factory used by this instance.
- public OpenIddictServerOwinHandler(
- IOpenIddictServerDispatcher dispatcher,
- IOpenIddictServerFactory factory)
- {
- _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
- _factory = factory ?? throw new ArgumentNullException(nameof(factory));
- }
+ /// The service provider.
+ public OpenIddictServerOwinHandler(IServiceProvider provider)
+ => _provider = provider ?? throw new ArgumentNullException(nameof(provider));
///
protected override async Task InitializeCoreAsync()
{
+ var dispatcher = _provider.GetRequiredService();
+
// Note: to ensure internal operations are not immediately cancelled when the request is aborted
// (which may represent a security risk if sensitive operations are in progress), an ad-hoc token
// source is always created and configured to be triggered 5 seconds after the request is aborted.
@@ -52,9 +49,16 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler(typeof(OpenIddictServerTransaction).FullName);
if (transaction is null)
{
+ var options = _provider.GetRequiredService>();
+
// Create a new transaction and attach the OWIN request to make it available to the OWIN handlers.
- transaction = await _factory.CreateTransactionAsync(source.Token);
- transaction.Properties[typeof(IOwinRequest).FullName!] = new WeakReference(Request);
+ transaction = new OpenIddictServerTransaction
+ {
+ CancellationToken = source.Token,
+ Options = options.CurrentValue,
+ Properties = { [typeof(IOwinRequest).FullName!] = Request },
+ ServiceProvider = _provider
+ };
// Attach the OpenIddict server transaction to the OWIN shared dictionary
// so that it can retrieved while performing sign-in/sign-out operations.
@@ -62,7 +66,7 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler();
+
var transaction = Context.Get(typeof(OpenIddictServerTransaction).FullName)
?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0112));
@@ -101,7 +107,7 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler
protected override async Task AuthenticateCoreAsync()
{
+ var dispatcher = _provider.GetRequiredService();
+
var transaction = Context.Get(typeof(OpenIddictServerTransaction).FullName)
?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0112));
@@ -131,7 +139,7 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler(typeof(ProcessAuthenticationContext).FullName!);
if (context is null)
{
- await _dispatcher.DispatchAsync(context = new ProcessAuthenticationContext(transaction));
+ await dispatcher.DispatchAsync(context = new ProcessAuthenticationContext(transaction));
// Store the context object in the transaction so it can be later retrieved by handlers
// that want to access the authentication result without triggering a new authentication flow.
@@ -279,6 +287,8 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler();
+
// Note: unlike the ASP.NET Core host, the OWIN host MUST check whether the status code
// corresponds to a challenge response, as LookupChallenge() will always return a non-null
// value when active authentication is used, even if no challenge was actually triggered.
@@ -295,7 +305,7 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler reference && reference.TryGetTarget(out IOwinRequest? request))
- {
- return request;
- }
-
- return null;
+ return transaction.Properties.TryGetValue(typeof(IOwinRequest).FullName!, out object? property)
+ && property is IOwinRequest request ? request : null;
}
///
diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs
index 1cece580..49d73de8 100644
--- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs
+++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinMiddleware.cs
@@ -5,17 +5,18 @@
*/
using System.ComponentModel;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Owin.Security.Infrastructure;
namespace OpenIddict.Server.Owin;
///
/// Provides the entry point necessary to register the OpenIddict server handler in an OWIN pipeline.
+///
+///
/// Note: this middleware is intended to be used with dependency injection containers
/// that support middleware resolution, like Autofac. Since it depends on scoped services,
/// it is NOT recommended to instantiate it as a singleton like a regular OWIN middleware.
-///
+///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictServerOwinMiddleware : AuthenticationMiddleware
{
@@ -37,8 +38,7 @@ public sealed class OpenIddictServerOwinMiddleware : AuthenticationMiddleware
/// A new instance of the class.
protected override AuthenticationHandler CreateHandler()
- => _provider.GetService()
- ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0122));
+ => new OpenIddictServerOwinHandler(_provider);
///
/// Provides the options used by the class.
diff --git a/src/OpenIddict.Server/IOpenIddictServerFactory.cs b/src/OpenIddict.Server/IOpenIddictServerFactory.cs
deleted file mode 100644
index 695333d1..00000000
--- a/src/OpenIddict.Server/IOpenIddictServerFactory.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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;
-
-namespace OpenIddict.Server;
-
-///
-/// Represents a service responsible for creating transactions.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public interface IOpenIddictServerFactory
-{
- ///
- /// Creates a new that is used as a
- /// way to store per-request data needed to process the requested operation.
- ///
- /// The that can be used to abort the operation.
- ///
- /// Note: the specified is automatically attached to the returned transaction.
- ///
- ///
- /// A that can be used to monitor the asynchronous
- /// operation, whose result returns the created transaction.
- ///
- ValueTask CreateTransactionAsync(CancellationToken cancellationToken);
-}
diff --git a/src/OpenIddict.Server/OpenIddictServerDispatcher.cs b/src/OpenIddict.Server/OpenIddictServerDispatcher.cs
index 6edc5576..be8884a0 100644
--- a/src/OpenIddict.Server/OpenIddictServerDispatcher.cs
+++ b/src/OpenIddict.Server/OpenIddictServerDispatcher.cs
@@ -6,7 +6,6 @@
using System.ComponentModel;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
namespace OpenIddict.Server;
@@ -16,113 +15,73 @@ namespace OpenIddict.Server;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictServerDispatcher : IOpenIddictServerDispatcher
{
- private readonly ILogger _logger;
- private readonly IOptionsMonitor _options;
- private readonly IServiceProvider _provider;
-
- ///
- /// Creates a new instance of the class.
- ///
- public OpenIddictServerDispatcher(
- ILogger logger,
- IOptionsMonitor options,
- IServiceProvider provider)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- _provider = provider ?? throw new ArgumentNullException(nameof(provider));
- }
-
///
public async ValueTask DispatchAsync(TContext context) where TContext : BaseContext
{
ArgumentNullException.ThrowIfNull(context);
- await foreach (var handler in GetHandlersAsync())
+ // Note: the descriptors collection is sorted during options initialization for performance reasons.
+ foreach (var descriptor in context.Options.Handlers)
{
context.CancellationToken.ThrowIfCancellationRequested();
+ if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor))
+ {
+ continue;
+ }
+
+ var handler = descriptor.ServiceDescriptor.ImplementationInstance as IOpenIddictServerHandler
+ ?? context.ServiceProvider.GetService(descriptor.ServiceDescriptor.ServiceType) as IOpenIddictServerHandler
+ ?? throw new InvalidOperationException(SR.FormatID0098(descriptor.ServiceDescriptor.ServiceType));
+
try
{
await handler.HandleAsync(context);
}
- catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception) && _logger.IsEnabled(LogLevel.Debug))
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception) && context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6132, exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName);
+ context.Logger.LogDebug(6132, exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName);
throw;
}
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6133, SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6133, SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName);
}
switch (context)
{
case BaseRequestContext { IsRequestHandled: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6134, SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6134, SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
case BaseRequestContext { IsRequestSkipped: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6135, SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6135, SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
case BaseValidatingContext { IsRejected: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6136, SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6136, SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
-
- default: continue;
- }
- }
-
- async IAsyncEnumerable> GetHandlersAsync()
- {
- // Note: the descriptors collection is sorted during options initialization for performance reasons.
- var descriptors = _options.CurrentValue.Handlers;
- if (descriptors.Count is 0)
- {
- yield break;
- }
-
- for (var index = 0; index < descriptors.Count; index++)
- {
- var descriptor = descriptors[index];
- if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor))
- {
- continue;
- }
-
- yield return descriptor.ServiceDescriptor switch
- {
- { ImplementationInstance: IOpenIddictServerHandler handler } => handler,
-
- _ when _provider.GetService(descriptor.ServiceDescriptor.ServiceType)
- is IOpenIddictServerHandler handler => handler,
-
- _ => throw new InvalidOperationException(SR.FormatID0098(descriptor.ServiceDescriptor.ServiceType))
- };
}
}
async ValueTask IsActiveAsync(OpenIddictServerHandlerDescriptor descriptor)
{
- for (var index = 0; index < descriptor.FilterTypes.Length; index++)
+ foreach (var type in descriptor.FilterTypes)
{
- if (_provider.GetService(descriptor.FilterTypes[index]) is not IOpenIddictServerHandlerFilter filter)
- {
- throw new InvalidOperationException(SR.FormatID0099(descriptor.FilterTypes[index]));
- }
+ var filter = context.ServiceProvider.GetService(type) as IOpenIddictServerHandlerFilter
+ ?? throw new InvalidOperationException(SR.FormatID0099(type));
if (!await filter.IsActiveAsync(context))
{
diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.cs b/src/OpenIddict.Server/OpenIddictServerEvents.cs
index dce4f282..cf6c6b46 100644
--- a/src/OpenIddict.Server/OpenIddictServerEvents.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Security.Claims;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Server;
@@ -64,12 +65,19 @@ public static partial class OpenIddictServerEvents
///
/// Gets the logger responsible for logging processed operations.
///
- public ILogger Logger => Transaction.Logger;
+ public ILogger Logger
+ => field ??= Transaction.ServiceProvider.GetRequiredService>();
///
/// Gets the OpenIddict server options.
///
public OpenIddictServerOptions Options => Transaction.Options;
+
+ ///
+ /// Gets the service provider associated with the current transaction.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public IServiceProvider ServiceProvider => Transaction.ServiceProvider;
}
///
diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
index 816a216c..baba1377 100644
--- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
@@ -29,8 +29,7 @@ public static class OpenIddictServerExtensions
builder.Services.AddLogging();
builder.Services.AddOptions();
- builder.Services.TryAddScoped();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddSingleton();
// Register the built-in server event handlers used by the OpenIddict server components.
// Note: the order used here is not important, as the actual order is set in the options.
diff --git a/src/OpenIddict.Server/OpenIddictServerFactory.cs b/src/OpenIddict.Server/OpenIddictServerFactory.cs
deleted file mode 100644
index 7c0394f6..00000000
--- a/src/OpenIddict.Server/OpenIddictServerFactory.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace OpenIddict.Server;
-
-///
-/// Represents a service responsible for creating transactions.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public sealed class OpenIddictServerFactory : IOpenIddictServerFactory
-{
- private readonly ILogger _logger;
- private readonly IOptionsMonitor _options;
-
- ///
- /// Creates a new instance of the class.
- ///
- public OpenIddictServerFactory(
- ILogger logger,
- IOptionsMonitor options)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- }
-
- ///
- public ValueTask CreateTransactionAsync(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new(Task.FromCanceled(cancellationToken));
- }
-
- return new(new OpenIddictServerTransaction
- {
- CancellationToken = cancellationToken,
- Logger = _logger,
- Options = _options.CurrentValue
- });
- }
-}
diff --git a/src/OpenIddict.Server/OpenIddictServerTransaction.cs b/src/OpenIddict.Server/OpenIddictServerTransaction.cs
index 49b63487..ad8780dd 100644
--- a/src/OpenIddict.Server/OpenIddictServerTransaction.cs
+++ b/src/OpenIddict.Server/OpenIddictServerTransaction.cs
@@ -6,20 +6,19 @@
using System.ComponentModel;
using System.Security.Cryptography.X509Certificates;
-using Microsoft.Extensions.Logging;
namespace OpenIddict.Server;
///
-/// Represents the context associated with an OpenID Connect server request.
+/// Represents the context associated with an OpenID Connect server operation.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictServerTransaction
{
///
- /// Gets or sets the cancellation token used to determine if the operation was aborted.
+ /// Gets the cancellation token used to determine if the operation was aborted.
///
- public CancellationToken CancellationToken { get; set; }
+ public required CancellationToken CancellationToken { get; init; }
///
/// Gets or sets the X.509 client certificate used by the remote peer, if available.
@@ -27,7 +26,7 @@ public sealed class OpenIddictServerTransaction
public X509Certificate2? RemoteCertificate { get; set; }
///
- /// Gets or sets the type of the endpoint processing the current request.
+ /// Gets or sets the type of the endpoint processing the current transaction.
///
public OpenIddictServerEndpointType EndpointType { get; set; }
@@ -42,17 +41,16 @@ public sealed class OpenIddictServerTransaction
public Uri? BaseUri { get; set; }
///
- /// Gets or sets the logger associated with the current request.
+ /// Gets the options associated with the current transaction.
///
- public ILogger Logger { get; set; } = default!;
+ public required OpenIddictServerOptions Options
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
///
- /// Gets or sets the options associated with the current request.
- ///
- public OpenIddictServerOptions Options { get; set; } = default!;
-
- ///
- /// Gets the additional properties associated with the current request.
+ /// Gets the additional properties associated with the current transaction.
///
public Dictionary Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
@@ -65,4 +63,13 @@ public sealed class OpenIddictServerTransaction
/// Gets or sets the current OpenID Connect response being returned.
///
public OpenIddictResponse? Response { get; set; }
+
+ ///
+ /// Gets the service provider used to resolve services.
+ ///
+ public required IServiceProvider ServiceProvider
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
}
diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreFeature.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreFeature.cs
index 456d6518..2fa0d704 100644
--- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreFeature.cs
+++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreFeature.cs
@@ -9,14 +9,17 @@ using System.ComponentModel;
namespace OpenIddict.Validation.AspNetCore;
///
-/// Exposes the current validation transaction to the ASP.NET Core host.
+/// Exposes the current validation transaction to the ASP.NET Core application.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictValidationAspNetCoreFeature
{
///
- /// Gets or sets the validation transaction that encapsulates all specific
- /// information about an individual OpenID Connect validation request.
+ /// Gets the transaction that encapsulates all specific information about an individual operation.
///
- public OpenIddictValidationTransaction? Transaction { get; set; }
+ public required OpenIddictValidationTransaction Transaction
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
}
diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs
index e9807f6c..c081c21f 100644
--- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs
+++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs
@@ -7,6 +7,7 @@
using System.ComponentModel;
using System.Security.Claims;
using System.Text.Encodings.Web;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using static OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreConstants;
@@ -22,22 +23,17 @@ public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandle
IAuthenticationRequestHandler
{
private readonly IOpenIddictValidationDispatcher _dispatcher;
- private readonly IOpenIddictValidationFactory _factory;
///
/// Creates a new instance of the class.
///
public OpenIddictValidationAspNetCoreHandler(
IOpenIddictValidationDispatcher dispatcher,
- IOpenIddictValidationFactory factory,
IOptionsMonitor options,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
- {
- _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
- _factory = factory ?? throw new ArgumentNullException(nameof(factory));
- }
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
public async Task HandleRequestAsync()
@@ -57,9 +53,16 @@ public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandle
var transaction = Context.Features.Get()?.Transaction;
if (transaction is null)
{
+ var options = Context.RequestServices.GetRequiredService>();
+
// Create a new transaction and attach the HTTP request to make it available to the ASP.NET Core handlers.
- transaction = await _factory.CreateTransactionAsync(source.Token);
- transaction.Properties[typeof(HttpRequest).FullName!] = new WeakReference(Request);
+ transaction = new OpenIddictValidationTransaction
+ {
+ CancellationToken = source.Token,
+ Options = options.CurrentValue,
+ Properties = { [typeof(HttpRequest).FullName!] = Request },
+ ServiceProvider = Context.RequestServices
+ };
// Attach the OpenIddict validation transaction to the ASP.NET Core features
// so that it can retrieved while performing challenge/forbid operations.
diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHelpers.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHelpers.cs
index a51cfce3..c6a54189 100644
--- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHelpers.cs
+++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHelpers.cs
@@ -23,17 +23,8 @@ public static class OpenIddictValidationAspNetCoreHelpers
{
ArgumentNullException.ThrowIfNull(transaction);
- if (!transaction.Properties.TryGetValue(typeof(HttpRequest).FullName!, out object? property))
- {
- return null;
- }
-
- if (property is WeakReference reference && reference.TryGetTarget(out HttpRequest? request))
- {
- return request;
- }
-
- return null;
+ return transaction.Properties.TryGetValue(typeof(HttpRequest).FullName!, out object? property)
+ && property is HttpRequest request ? request : null;
}
///
@@ -52,7 +43,7 @@ public static class OpenIddictValidationAspNetCoreHelpers
/// Retrieves the instance stored in .
///
/// The context instance.
- /// The instance or null if it couldn't be found.
+ /// The instance or if it couldn't be found.
public static OpenIddictRequest? GetOpenIddictValidationRequest(this HttpContext context)
{
ArgumentNullException.ThrowIfNull(context);
@@ -64,7 +55,7 @@ public static class OpenIddictValidationAspNetCoreHelpers
/// Retrieves the instance stored in .
///
/// The context instance.
- /// The instance or null if it couldn't be found.
+ /// The instance or if it couldn't be found.
public static OpenIddictResponse? GetOpenIddictValidationResponse(this HttpContext context)
{
ArgumentNullException.ThrowIfNull(context);
diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs
index 1266cb9e..e330061f 100644
--- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs
+++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinExtensions.cs
@@ -29,7 +29,6 @@ public static class OpenIddictValidationOwinExtensions
// Note: unlike regular OWIN middleware, the OpenIddict validation middleware is registered
// as a scoped service in the DI container. This allows containers that support middleware
// resolution (like Autofac) to use it without requiring additional configuration.
- builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
// Register the built-in event handlers used by the OpenIddict OWIN validation components.
diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs
index d6612fa8..e93b5f2a 100644
--- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs
+++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs
@@ -6,6 +6,8 @@
using System.ComponentModel;
using System.Security.Claims;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Microsoft.Owin.Security.Infrastructure;
using static OpenIddict.Validation.Owin.OpenIddictValidationOwinConstants;
using Properties = OpenIddict.Validation.Owin.OpenIddictValidationOwinConstants.Properties;
@@ -18,25 +20,20 @@ namespace OpenIddict.Validation.Owin;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler
{
- private readonly IOpenIddictValidationDispatcher _dispatcher;
- private readonly IOpenIddictValidationFactory _factory;
+ private readonly IServiceProvider _provider;
///
/// Creates a new instance of the class.
///
- /// The OpenIddict validation provider used by this instance.
- /// The OpenIddict validation factory used by this instance.
- public OpenIddictValidationOwinHandler(
- IOpenIddictValidationDispatcher dispatcher,
- IOpenIddictValidationFactory factory)
- {
- _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
- _factory = factory ?? throw new ArgumentNullException(nameof(factory));
- }
+ /// The service provider.
+ public OpenIddictValidationOwinHandler(IServiceProvider provider)
+ => _provider = provider ?? throw new ArgumentNullException(nameof(provider));
///
protected override async Task InitializeCoreAsync()
{
+ var dispatcher = _provider.GetRequiredService();
+
// Note: to ensure internal operations are not immediately cancelled when the request is aborted
// (which may represent a security risk if sensitive operations are in progress), an ad-hoc token
// source is always created and configured to be triggered 5 seconds after the request is aborted.
@@ -52,9 +49,16 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler(typeof(OpenIddictValidationTransaction).FullName);
if (transaction is null)
{
+ var options = _provider.GetRequiredService>();
+
// Create a new transaction and attach the OWIN request to make it available to the OWIN handlers.
- transaction = await _factory.CreateTransactionAsync(source.Token);
- transaction.Properties[typeof(IOwinRequest).FullName!] = new WeakReference(Request);
+ transaction = new OpenIddictValidationTransaction
+ {
+ CancellationToken = source.Token,
+ Options = options.CurrentValue,
+ Properties = { [typeof(IOwinRequest).FullName!] = Request },
+ ServiceProvider = _provider
+ };
// Attach the OpenIddict validation transaction to the OWIN shared dictionary
// so that it can retrieved while performing sign-in/sign-out operations.
@@ -62,7 +66,7 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler();
+
var transaction = Context.Get(typeof(OpenIddictValidationTransaction).FullName)
?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0166));
@@ -101,7 +107,7 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler
protected override async Task AuthenticateCoreAsync()
{
+ var dispatcher = _provider.GetRequiredService();
+
var transaction = Context.Get(typeof(OpenIddictValidationTransaction).FullName)
?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0166));
@@ -131,7 +139,7 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler(typeof(ProcessAuthenticationContext).FullName!);
if (context is null)
{
- await _dispatcher.DispatchAsync(context = new ProcessAuthenticationContext(transaction));
+ await dispatcher.DispatchAsync(context = new ProcessAuthenticationContext(transaction));
// Store the context object in the transaction so it can be later retrieved by handlers
// that want to access the authentication result without triggering a new authentication flow.
@@ -211,6 +219,8 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler();
+
// Note: unlike the ASP.NET Core host, the OWIN host MUST check whether the status code
// corresponds to a challenge response, as LookupChallenge() will always return a non-null
// value when active authentication is used, even if no challenge was actually triggered.
@@ -227,7 +237,7 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler reference && reference.TryGetTarget(out IOwinRequest? request))
- {
- return request;
- }
-
- return null;
+ return transaction.Properties.TryGetValue(typeof(IOwinRequest).FullName!, out object? property)
+ && property is IOwinRequest request ? request : null;
}
///
diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs
index 28ed56db..2524c240 100644
--- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs
+++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinMiddleware.cs
@@ -13,10 +13,12 @@ namespace OpenIddict.Validation.Owin;
///
/// Provides the entry point necessary to register the OpenIddict validation handler in an OWIN pipeline.
+///
+///
/// Note: this middleware is intended to be used with dependency injection containers
/// that support middleware resolution, like Autofac. Since it depends on scoped services,
/// it is NOT recommended to instantiate it as a singleton like a regular OWIN middleware.
-///
+///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictValidationOwinMiddleware : AuthenticationMiddleware
{
@@ -32,9 +34,8 @@ public sealed class OpenIddictValidationOwinMiddleware : AuthenticationMiddlewar
IServiceProvider provider)
: base(next, new InternalOptions()
{
- AuthenticationMode = provider.GetService>()
- ?.CurrentValue.AuthenticationMode
- ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0169))
+ AuthenticationMode = provider.GetRequiredService>()
+ .CurrentValue.AuthenticationMode
})
=> _provider = provider ?? throw new ArgumentNullException(nameof(provider));
@@ -43,8 +44,7 @@ public sealed class OpenIddictValidationOwinMiddleware : AuthenticationMiddlewar
///
/// A new instance of the class.
protected override AuthenticationHandler CreateHandler()
- => _provider.GetService()
- ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0169));
+ => new OpenIddictValidationOwinHandler(_provider);
///
/// Provides the options used by the class.
diff --git a/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs b/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs
deleted file mode 100644
index 5ad893d4..00000000
--- a/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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;
-
-namespace OpenIddict.Validation;
-
-///
-/// Represents a service responsible for creating transactions.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public interface IOpenIddictValidationFactory
-{
- ///
- /// Creates a new that is used as a
- /// way to store per-request data needed to process the requested operation.
- ///
- /// The that can be used to abort the operation.
- ///
- /// Note: the specified is automatically attached to the returned transaction.
- ///
- ///
- /// A that can be used to monitor the asynchronous
- /// operation, whose result returns the created transaction.
- ///
- ValueTask CreateTransactionAsync(CancellationToken cancellationToken);
-}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs b/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs
index b0fc80e1..3cc2012c 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs
@@ -6,7 +6,6 @@
using System.ComponentModel;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
namespace OpenIddict.Validation;
@@ -16,113 +15,73 @@ namespace OpenIddict.Validation;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictValidationDispatcher : IOpenIddictValidationDispatcher
{
- private readonly ILogger _logger;
- private readonly IOptionsMonitor _options;
- private readonly IServiceProvider _provider;
-
- ///
- /// Creates a new instance of the class.
- ///
- public OpenIddictValidationDispatcher(
- ILogger logger,
- IOptionsMonitor options,
- IServiceProvider provider)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- _provider = provider ?? throw new ArgumentNullException(nameof(provider));
- }
-
///
public async ValueTask DispatchAsync(TContext context) where TContext : BaseContext
{
ArgumentNullException.ThrowIfNull(context);
- await foreach (var handler in GetHandlersAsync())
+ // Note: the descriptors collection is sorted during options initialization for performance reasons.
+ foreach (var descriptor in context.Options.Handlers)
{
context.CancellationToken.ThrowIfCancellationRequested();
+ if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor))
+ {
+ continue;
+ }
+
+ var handler = descriptor.ServiceDescriptor.ImplementationInstance as IOpenIddictValidationHandler
+ ?? context.ServiceProvider.GetService(descriptor.ServiceDescriptor.ServiceType) as IOpenIddictValidationHandler
+ ?? throw new InvalidOperationException(SR.FormatID0098(descriptor.ServiceDescriptor.ServiceType));
+
try
{
await handler.HandleAsync(context);
}
- catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception) && _logger.IsEnabled(LogLevel.Debug))
+ catch (Exception exception) when (!OpenIddictHelpers.IsFatal(exception) && context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6132, exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName);
+ context.Logger.LogDebug(6132, exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName);
throw;
}
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6133, SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6133, SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName);
}
switch (context)
{
case BaseRequestContext { IsRequestHandled: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6134, SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6134, SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
case BaseRequestContext { IsRequestSkipped: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6135, SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6135, SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
case BaseValidatingContext { IsRejected: true }:
- if (_logger.IsEnabled(LogLevel.Debug))
+ if (context.Logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug(6136, SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName);
+ context.Logger.LogDebug(6136, SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName);
}
return;
-
- default: continue;
- }
- }
-
- async IAsyncEnumerable> GetHandlersAsync()
- {
- // Note: the descriptors collection is sorted during options initialization for performance reasons.
- var descriptors = _options.CurrentValue.Handlers;
- if (descriptors.Count is 0)
- {
- yield break;
- }
-
- for (var index = 0; index < descriptors.Count; index++)
- {
- var descriptor = descriptors[index];
- if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor))
- {
- continue;
- }
-
- yield return descriptor.ServiceDescriptor switch
- {
- { ImplementationInstance: IOpenIddictValidationHandler handler } => handler,
-
- _ when _provider.GetService(descriptor.ServiceDescriptor.ServiceType)
- is IOpenIddictValidationHandler handler => handler,
-
- _ => throw new InvalidOperationException(SR.FormatID0138(descriptor.ServiceDescriptor.ServiceType))
- };
}
}
async ValueTask IsActiveAsync(OpenIddictValidationHandlerDescriptor descriptor)
{
- for (var index = 0; index < descriptor.FilterTypes.Length; index++)
+ foreach (var type in descriptor.FilterTypes)
{
- if (_provider.GetService(descriptor.FilterTypes[index]) is not IOpenIddictValidationHandlerFilter filter)
- {
- throw new InvalidOperationException(SR.FormatID0099(descriptor.FilterTypes[index]));
- }
+ var filter = context.ServiceProvider.GetService(type) as IOpenIddictValidationHandlerFilter
+ ?? throw new InvalidOperationException(SR.FormatID0099(type));
if (!await filter.IsActiveAsync(context))
{
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEvents.cs b/src/OpenIddict.Validation/OpenIddictValidationEvents.cs
index 0fd03904..4c5c62e7 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationEvents.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationEvents.cs
@@ -7,6 +7,7 @@
using System.ComponentModel;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Validation;
@@ -74,12 +75,19 @@ public static partial class OpenIddictValidationEvents
///
/// Gets the logger responsible for logging processed operations.
///
- public ILogger Logger => Transaction.Logger;
+ public ILogger Logger
+ => field ??= Transaction.ServiceProvider.GetRequiredService>();
///
/// Gets the OpenIddict validation options.
///
public OpenIddictValidationOptions Options => Transaction.Options;
+
+ ///
+ /// Gets the service provider associated with the current transaction.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public IServiceProvider ServiceProvider => Transaction.ServiceProvider;
}
///
diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
index 787284b5..67e1f76c 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
@@ -30,8 +30,7 @@ public static class OpenIddictValidationExtensions
builder.Services.AddOptions();
builder.Services.TryAddSingleton();
- builder.Services.TryAddScoped();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddSingleton();
// Register the built-in validation event handlers used by the OpenIddict validation components.
// Note: the order used here is not important, as the actual order is set in the options.
diff --git a/src/OpenIddict.Validation/OpenIddictValidationFactory.cs b/src/OpenIddict.Validation/OpenIddictValidationFactory.cs
deleted file mode 100644
index 4b806f42..00000000
--- a/src/OpenIddict.Validation/OpenIddictValidationFactory.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace OpenIddict.Validation;
-
-///
-/// Represents a service responsible for creating transactions.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public sealed class OpenIddictValidationFactory : IOpenIddictValidationFactory
-{
- private readonly ILogger _logger;
- private readonly IOptionsMonitor _options;
-
- ///
- /// Creates a new instance of the class.
- ///
- public OpenIddictValidationFactory(
- ILogger logger,
- IOptionsMonitor options)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- _options = options ?? throw new ArgumentNullException(nameof(options));
- }
-
- ///
- public ValueTask CreateTransactionAsync(CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return new(Task.FromCanceled(cancellationToken));
- }
-
- return new(new OpenIddictValidationTransaction
- {
- CancellationToken = cancellationToken,
- Logger = _logger,
- Options = _options.CurrentValue
- });
- }
-}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationService.cs b/src/OpenIddict.Validation/OpenIddictValidationService.cs
index b3da7d7a..b01cda86 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationService.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationService.cs
@@ -9,6 +9,7 @@ using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Abstractions.OpenIddictExceptions;
@@ -40,14 +41,17 @@ public class OpenIddictValidationService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictValidationTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = _provider
+ };
var context = new ProcessAuthenticationContext(transaction)
{
@@ -85,14 +89,17 @@ public class OpenIddictValidationService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictValidationTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = _provider
+ };
var request = new OpenIddictRequest();
request = await PrepareConfigurationRequestAsync();
@@ -207,14 +214,17 @@ public class OpenIddictValidationService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictValidationTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = _provider
+ };
var request = new OpenIddictRequest();
request = await PrepareJsonWebKeySetRequestAsync();
@@ -337,14 +347,17 @@ public class OpenIddictValidationService
cancellationToken.ThrowIfCancellationRequested();
- // Note: this service is registered as a singleton service. As such, it cannot
- // directly depend on scoped services like the event dispatcher. To work around
- // this limitation, a scope is manually created for each method to this service.
await using var scope = _provider.CreateAsyncScope();
var dispatcher = scope.ServiceProvider.GetRequiredService();
- var factory = scope.ServiceProvider.GetRequiredService();
- var transaction = await factory.CreateTransactionAsync(cancellationToken);
+ var options = scope.ServiceProvider.GetRequiredService>();
+
+ var transaction = new OpenIddictValidationTransaction
+ {
+ CancellationToken = cancellationToken,
+ Options = options.CurrentValue,
+ ServiceProvider = _provider
+ };
request = await PrepareIntrospectionRequestAsync();
request = await ApplyIntrospectionRequestAsync();
diff --git a/src/OpenIddict.Validation/OpenIddictValidationTransaction.cs b/src/OpenIddict.Validation/OpenIddictValidationTransaction.cs
index 341f851a..2d5016d0 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationTransaction.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationTransaction.cs
@@ -6,20 +6,19 @@
using System.ComponentModel;
using System.Security.Cryptography.X509Certificates;
-using Microsoft.Extensions.Logging;
namespace OpenIddict.Validation;
///
-/// Represents the context associated with an OpenID Connect validation request.
+/// Represents the context associated with an OpenID Connect validation operation.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictValidationTransaction
{
///
- /// Gets or sets the cancellation token used to determine if the operation was aborted.
+ /// Gets the cancellation token used to determine if the operation was aborted.
///
- public CancellationToken CancellationToken { get; set; }
+ public required CancellationToken CancellationToken { get; init; }
///
/// Gets or sets the X.509 client certificate used by the remote peer, if available.
@@ -27,7 +26,7 @@ public sealed class OpenIddictValidationTransaction
public X509Certificate2? RemoteCertificate { get; set; }
///
- /// Gets or sets the type of the endpoint processing the current request.
+ /// Gets or sets the type of the endpoint processing the current transaction.
///
public OpenIddictValidationEndpointType EndpointType { get; set; }
@@ -42,24 +41,27 @@ public sealed class OpenIddictValidationTransaction
public Uri? BaseUri { get; set; }
///
- /// Gets or sets the logger associated with the current request.
+ /// Gets the options associated with the current transaction.
///
- public ILogger Logger { get; set; } = default!;
+ public required OpenIddictValidationOptions Options
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
///
- /// Gets or sets the options associated with the current request.
- ///
- public OpenIddictValidationOptions Options { get; set; } = default!;
-
- ///
- /// Gets the additional properties associated with the current request.
+ /// Gets the additional properties associated with the current transaction.
///
public Dictionary Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
///
- /// Gets or sets the server configuration used for the current request.
+ /// Gets or sets the server configuration used for the current transaction.
///
- public OpenIddictConfiguration Configuration { get; set; } = default!;
+ public OpenIddictConfiguration Configuration
+ {
+ get;
+ set { ArgumentNullException.ThrowIfNull(value); field = value; }
+ } = default!;
///
/// Gets or sets the current OpenID Connect request.
@@ -70,4 +72,13 @@ public sealed class OpenIddictValidationTransaction
/// Gets or sets the current OpenID Connect response being returned.
///
public OpenIddictResponse? Response { get; set; }
+
+ ///
+ /// Gets the service provider used to resolve services.
+ ///
+ public required IServiceProvider ServiceProvider
+ {
+ get;
+ init { ArgumentNullException.ThrowIfNull(value); field = value; }
+ }
}
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
index b8de9d76..474958d4 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
@@ -79,23 +79,7 @@ public class OpenIddictServerExtensionsTests
// Assert
Assert.Contains(services, service => service.ServiceType == typeof(IOpenIddictServerDispatcher) &&
service.ImplementationType == typeof(OpenIddictServerDispatcher) &&
- service.Lifetime is ServiceLifetime.Scoped);
- }
-
- [Fact]
- public void AddServer_RegistersServerFactory()
- {
- // Arrange
- var services = new ServiceCollection();
- var builder = new OpenIddictBuilder(services);
-
- // Act
- builder.AddServer();
-
- // Assert
- Assert.Contains(services, service => service.ServiceType == typeof(IOpenIddictServerFactory) &&
- service.ImplementationType == typeof(OpenIddictServerFactory) &&
- service.Lifetime is ServiceLifetime.Scoped);
+ service.Lifetime is ServiceLifetime.Singleton);
}
public static IEnumerable