diff --git a/src/OpenIddict.Abstractions/OpenIddictBuilder.cs b/src/OpenIddict.Abstractions/OpenIddictBuilder.cs
index 88acb436..ab10dd05 100644
--- a/src/OpenIddict.Abstractions/OpenIddictBuilder.cs
+++ b/src/OpenIddict.Abstractions/OpenIddictBuilder.cs
@@ -35,4 +35,4 @@ namespace Microsoft.Extensions.DependencyInjection
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
index 73fd5546..25112337 100644
--- a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
+++ b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
@@ -779,4 +779,4 @@ namespace Microsoft.Extensions.DependencyInjection
return Configure(options => options.DefaultTokenType = type);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/IOpenIddictServerEvent.cs b/src/OpenIddict.Server/IOpenIddictServerEvent.cs
new file mode 100644
index 00000000..6b371320
--- /dev/null
+++ b/src/OpenIddict.Server/IOpenIddictServerEvent.cs
@@ -0,0 +1,7 @@
+namespace OpenIddict.Server
+{
+ ///
+ /// Represents an OpenIddict server event.
+ ///
+ public interface IOpenIddictServerEvent { }
+}
diff --git a/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs b/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
new file mode 100644
index 00000000..d620c2ff
--- /dev/null
+++ b/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
@@ -0,0 +1,25 @@
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Represents a handler able to process events.
+ ///
+ /// The type of the events handled by this instance.
+ public interface IOpenIddictServerEventHandler where TEvent : class, IOpenIddictServerEvent
+ {
+ ///
+ /// Processes the event.
+ ///
+ /// The event to process.
+ ///
+ /// The that can be used to abort the operation.
+ ///
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/OpenIddict.Server/IOpenIddictServerEventService.cs b/src/OpenIddict.Server/IOpenIddictServerEventService.cs
new file mode 100644
index 00000000..0549bf1f
--- /dev/null
+++ b/src/OpenIddict.Server/IOpenIddictServerEventService.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Dispatches events by invoking the corresponding handlers.
+ ///
+ public interface IOpenIddictServerEventService
+ {
+ ///
+ /// Publishes a new event.
+ ///
+ /// The type of the event to publish.
+ /// The event to publish.
+ /// The that can be used to abort the operation.
+ /// A that can be used to monitor the asynchronous operation.
+ Task PublishAsync([NotNull] TEvent notification, CancellationToken cancellationToken = default)
+ where TEvent : class, IOpenIddictServerEvent;
+ }
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs b/src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs
index d4702720..79405eec 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs
@@ -1,8 +1,5 @@
-using System;
-using System.ComponentModel;
-using System.Text;
+using System.ComponentModel;
using System.Text.Encodings.Web;
-using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
@@ -22,36 +19,5 @@ namespace OpenIddict.Server
: base(options, logger, encoder, clock)
{
}
-
- protected override async Task InitializeEventsAsync()
- {
- await base.InitializeEventsAsync();
-
- // If an application provider instance or type was specified, import the application provider events.
- if (Options.ApplicationProvider != null || Options.ApplicationProviderType != null)
- {
- // Resolve the user provider from the options or from the services container.
- var provider = Options.ApplicationProvider;
- if (provider == null)
- {
- provider = Context.RequestServices.GetService(Options.ApplicationProviderType) as OpenIdConnectServerProvider;
- }
-
- if (provider == null)
- {
- throw new InvalidOperationException(new StringBuilder()
- .AppendLine("The application provider cannot be resolved from the dependency injection container. ")
- .Append("Make sure it is correctly registered in 'ConfigureServices(IServiceCollection services)'.")
- .ToString());
- }
-
- // Update the main provider to invoke the user provider's event handlers.
- Provider.Import(provider);
- }
- }
-
- private new OpenIddictServerOptions Options => (OpenIddictServerOptions) base.Options;
-
- private OpenIddictServerProvider Provider => (OpenIddictServerProvider) base.Events;
}
}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs b/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs
index 1f908f0f..4556ccd9 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs
@@ -63,19 +63,6 @@ namespace OpenIddict.Server
throw new InvalidOperationException("A random number generator must be registered.");
}
- if (options.ApplicationProviderType != null)
- {
- if (options.ApplicationProvider != null)
- {
- throw new InvalidOperationException("An application provider cannot be registered when a type is specified.");
- }
-
- if (!typeof(OpenIdConnectServerProvider).IsAssignableFrom(options.ApplicationProviderType))
- {
- throw new InvalidOperationException("Application providers must inherit from OpenIdConnectServerProvider.");
- }
- }
-
// When no distributed cache has been registered in the options,
// try to resolve it from the dependency injection container.
if (options.Cache == null)
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
index e519ffc9..fd7fcb51 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
@@ -107,7 +107,7 @@ namespace OpenIddict.Server
}
}
- await base.ExtractAuthorizationRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ExtractAuthorizationRequest(context));
}
public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context)
@@ -433,7 +433,7 @@ namespace OpenIddict.Server
context.Validate();
- await base.ValidateAuthorizationRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateAuthorizationRequest(context));
}
public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context)
@@ -485,7 +485,7 @@ namespace OpenIddict.Server
return;
}
- await base.HandleAuthorizationRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.HandleAuthorizationRequest(context));
}
public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context)
@@ -526,7 +526,7 @@ namespace OpenIddict.Server
}
}
- await base.ApplyAuthorizationResponse(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ApplyAuthorizationResponse(context));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs
index 6dba5d7f..174eb8dc 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs
@@ -13,6 +13,12 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
+ public override Task ExtractConfigurationRequest([NotNull] ExtractConfigurationRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ExtractConfigurationRequest(context));
+
+ public override Task ValidateConfigurationRequest([NotNull] ValidateConfigurationRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ValidateConfigurationRequest(context));
+
public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@@ -41,7 +47,22 @@ namespace OpenIddict.Server
context.Metadata[OpenIdConnectConstants.Metadata.RequestParameterSupported] = false;
context.Metadata[OpenIdConnectConstants.Metadata.RequestUriParameterSupported] = false;
- return base.HandleConfigurationRequest(context);
+ return _eventService.PublishAsync(new OpenIddictServerEvents.HandleConfigurationRequest(context));
}
+
+ public override Task ApplyConfigurationResponse([NotNull] ApplyConfigurationResponseContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ApplyConfigurationResponse(context));
+
+ public override Task ExtractCryptographyRequest([NotNull] ExtractCryptographyRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ExtractCryptographyRequest(context));
+
+ public override Task ValidateCryptographyRequest([NotNull] ValidateCryptographyRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ValidateCryptographyRequest(context));
+
+ public override Task HandleCryptographyRequest([NotNull] HandleCryptographyRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.HandleCryptographyRequest(context));
+
+ public override Task ApplyCryptographyResponse([NotNull] ApplyCryptographyResponseContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ApplyCryptographyResponse(context));
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs
index 664fa0bb..c8d3bbe6 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs
@@ -20,6 +20,9 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
+ public override Task ExtractTokenRequest([NotNull] ExtractTokenRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ExtractTokenRequest(context));
+
public override async Task ValidateTokenRequest([NotNull] ValidateTokenRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@@ -289,7 +292,7 @@ namespace OpenIddict.Server
context.Validate();
- await base.ValidateTokenRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateTokenRequest(context));
}
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context)
@@ -308,7 +311,7 @@ namespace OpenIddict.Server
// the user code to handle the token request.
context.SkipHandler();
- await base.HandleTokenRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.HandleTokenRequest(context));
return;
}
@@ -400,7 +403,10 @@ namespace OpenIddict.Server
// the user code to handle the token request.
context.SkipHandler();
- await base.HandleTokenRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.HandleTokenRequest(context));
}
+
+ public override Task ApplyTokenResponse([NotNull] ApplyTokenResponseContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ApplyTokenResponse(context));
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
index fa2594a1..62680912 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
@@ -648,4 +648,4 @@ namespace OpenIddict.Server
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs
index e33d9bf5..ee3fde91 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs
@@ -17,6 +17,9 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
+ public override Task ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ExtractIntrospectionRequest(context));
+
public override async Task ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@@ -94,7 +97,7 @@ namespace OpenIddict.Server
context.Validate();
- await base.ValidateIntrospectionRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateIntrospectionRequest(context));
}
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context)
@@ -177,7 +180,10 @@ namespace OpenIddict.Server
}
}
- await base.HandleIntrospectionRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.HandleIntrospectionRequest(context));
}
+
+ public override Task ApplyIntrospectionResponse([NotNull] ApplyIntrospectionResponseContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ApplyIntrospectionResponse(context));
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs
index 1f69dcba..e7881fd3 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs
@@ -18,6 +18,9 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
+ public override Task ExtractRevocationRequest([NotNull] ExtractRevocationRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ExtractRevocationRequest(context));
+
public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@@ -160,7 +163,7 @@ namespace OpenIddict.Server
context.Validate();
- await base.ValidateRevocationRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateRevocationRequest(context));
}
public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context)
@@ -232,7 +235,10 @@ namespace OpenIddict.Server
context.Revoked = true;
- await base.HandleRevocationRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.HandleRevocationRequest(context));
}
+
+ public override Task ApplyRevocationResponse([NotNull] ApplyRevocationResponseContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ApplyRevocationResponse(context));
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs
index 838eb32f..a06dbcb0 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs
@@ -34,7 +34,7 @@ namespace OpenIddict.Server
context.HandleDeserialization();
}
- await base.DeserializeAccessToken(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeAccessToken(context));
}
public override async Task DeserializeAuthorizationCode([NotNull] DeserializeAuthorizationCodeContext context)
@@ -53,9 +53,12 @@ namespace OpenIddict.Server
// Prevent the OpenID Connect server middleware from using its default logic.
context.HandleDeserialization();
- await base.DeserializeAuthorizationCode(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeAuthorizationCode(context));
}
+ public override Task DeserializeIdentityToken(DeserializeIdentityTokenContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeIdentityToken(context));
+
public override async Task DeserializeRefreshToken([NotNull] DeserializeRefreshTokenContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@@ -72,7 +75,7 @@ namespace OpenIddict.Server
// Prevent the OpenID Connect server middleware from using its default logic.
context.HandleDeserialization();
- await base.DeserializeRefreshToken(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeRefreshToken(context));
}
public override async Task SerializeAccessToken([NotNull] SerializeAccessTokenContext context)
@@ -98,7 +101,7 @@ namespace OpenIddict.Server
// Otherwise, let the OpenID Connect server middleware
// serialize the token using its default internal logic.
- await base.SerializeAccessToken(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.SerializeAccessToken(context));
}
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context)
@@ -126,9 +129,12 @@ namespace OpenIddict.Server
// Otherwise, let the OpenID Connect server middleware
// serialize the token using its default internal logic.
- await base.SerializeAuthorizationCode(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.SerializeAuthorizationCode(context));
}
+ public override Task SerializeIdentityToken(SerializeIdentityTokenContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.SerializeIdentityToken(context));
+
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@@ -154,7 +160,7 @@ namespace OpenIddict.Server
// Otherwise, let the OpenID Connect server middleware
// serialize the token using its default internal logic.
- await base.SerializeRefreshToken(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.SerializeRefreshToken(context));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs
index 7feaf96a..2bd4c543 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs
@@ -77,7 +77,7 @@ namespace OpenIddict.Server
}
}
- await base.ExtractLogoutRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ExtractLogoutRequest(context));
}
public override async Task ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context)
@@ -151,7 +151,7 @@ namespace OpenIddict.Server
context.Validate();
- await base.ValidateLogoutRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateLogoutRequest(context));
}
public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context)
@@ -203,7 +203,7 @@ namespace OpenIddict.Server
return;
}
- await base.HandleLogoutRequest(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.HandleLogoutRequest(context));
}
public override async Task ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context)
@@ -244,7 +244,7 @@ namespace OpenIddict.Server
}
}
- await base.ApplyLogoutResponse(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ApplyLogoutResponse(context));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs
index 577bfad0..c28196f3 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs
@@ -24,7 +24,16 @@ namespace OpenIddict.Server
// the user code to handle the userinfo request.
context.SkipHandler();
- return base.ExtractUserinfoRequest(context);
+ return _eventService.PublishAsync(new OpenIddictServerEvents.ExtractUserinfoRequest(context));
}
+
+ public override Task ValidateUserinfoRequest([NotNull] ValidateUserinfoRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ValidateUserinfoRequest(context));
+
+ public override Task HandleUserinfoRequest([NotNull] HandleUserinfoRequestContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.HandleUserinfoRequest(context));
+
+ public override Task ApplyUserinfoResponse([NotNull] ApplyUserinfoResponseContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.ApplyUserinfoResponse(context));
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
index 868dfcb9..5db8fcbf 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
@@ -24,26 +24,32 @@ namespace OpenIddict.Server
[EditorBrowsable(EditorBrowsableState.Never)]
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
- public readonly ILogger _logger;
- public readonly IOpenIddictApplicationManager _applicationManager;
- public readonly IOpenIddictAuthorizationManager _authorizationManager;
- public readonly IOpenIddictScopeManager _scopeManager;
- public readonly IOpenIddictTokenManager _tokenManager;
+ private readonly ILogger _logger;
+ private readonly IOpenIddictServerEventService _eventService;
+ private readonly IOpenIddictApplicationManager _applicationManager;
+ private readonly IOpenIddictAuthorizationManager _authorizationManager;
+ private readonly IOpenIddictScopeManager _scopeManager;
+ private readonly IOpenIddictTokenManager _tokenManager;
public OpenIddictServerProvider(
[NotNull] ILogger logger,
+ [NotNull] IOpenIddictServerEventService eventService,
[NotNull] IOpenIddictApplicationManager applicationManager,
[NotNull] IOpenIddictAuthorizationManager authorizationManager,
[NotNull] IOpenIddictScopeManager scopeManager,
[NotNull] IOpenIddictTokenManager tokenManager)
{
_logger = logger;
+ _eventService = eventService;
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_scopeManager = scopeManager;
_tokenManager = tokenManager;
}
+ public override Task MatchEndpoint([NotNull] MatchEndpointContext context)
+ => _eventService.PublishAsync(new OpenIddictServerEvents.MatchEndpoint(context));
+
public override Task ProcessChallengeResponse([NotNull] ProcessChallengeResponseContext context)
{
Debug.Assert(context.Request.IsAuthorizationRequest() ||
@@ -58,7 +64,7 @@ namespace OpenIddict.Server
context.Response.AddParameter(parameter, value);
}
- return base.ProcessChallengeResponse(context);
+ return _eventService.PublishAsync(new OpenIddictServerEvents.ProcessChallengeResponse(context));
}
public override async Task ProcessSigninResponse([NotNull] ProcessSigninResponseContext context)
@@ -188,7 +194,7 @@ namespace OpenIddict.Server
context.Ticket.RemoveProperty(property);
}
- await base.ProcessSigninResponse(context);
+ await _eventService.PublishAsync(new OpenIddictServerEvents.ProcessSigninResponse(context));
}
public override Task ProcessSignoutResponse([NotNull] ProcessSignoutResponseContext context)
@@ -202,61 +208,7 @@ namespace OpenIddict.Server
context.Response.AddParameter(parameter, value);
}
- return base.ProcessSignoutResponse(context);
- }
-
- public void Import([NotNull] OpenIdConnectServerProvider provider)
- {
- OnMatchEndpoint = provider.MatchEndpoint;
-
- OnExtractAuthorizationRequest = provider.ExtractAuthorizationRequest;
- OnExtractConfigurationRequest = provider.ExtractConfigurationRequest;
- OnExtractCryptographyRequest = provider.ExtractCryptographyRequest;
- OnExtractIntrospectionRequest = provider.ExtractIntrospectionRequest;
- OnExtractLogoutRequest = provider.ExtractLogoutRequest;
- OnExtractRevocationRequest = provider.ExtractRevocationRequest;
- OnExtractTokenRequest = provider.ExtractTokenRequest;
- OnExtractUserinfoRequest = provider.ExtractUserinfoRequest;
- OnValidateAuthorizationRequest = provider.ValidateAuthorizationRequest;
- OnValidateConfigurationRequest = provider.ValidateConfigurationRequest;
- OnValidateCryptographyRequest = provider.ValidateCryptographyRequest;
- OnValidateIntrospectionRequest = provider.ValidateIntrospectionRequest;
- OnValidateLogoutRequest = provider.ValidateLogoutRequest;
- OnValidateRevocationRequest = provider.ValidateRevocationRequest;
- OnValidateTokenRequest = provider.ValidateTokenRequest;
- OnValidateUserinfoRequest = provider.ValidateUserinfoRequest;
-
- OnHandleAuthorizationRequest = provider.HandleAuthorizationRequest;
- OnHandleConfigurationRequest = provider.HandleConfigurationRequest;
- OnHandleCryptographyRequest = provider.HandleCryptographyRequest;
- OnHandleIntrospectionRequest = provider.HandleIntrospectionRequest;
- OnHandleLogoutRequest = provider.HandleLogoutRequest;
- OnHandleRevocationRequest = provider.HandleRevocationRequest;
- OnHandleTokenRequest = provider.HandleTokenRequest;
- OnHandleUserinfoRequest = provider.HandleUserinfoRequest;
-
- OnApplyAuthorizationResponse = provider.ApplyAuthorizationResponse;
- OnApplyConfigurationResponse = provider.ApplyConfigurationResponse;
- OnApplyCryptographyResponse = provider.ApplyCryptographyResponse;
- OnApplyIntrospectionResponse = provider.ApplyIntrospectionResponse;
- OnApplyLogoutResponse = provider.ApplyLogoutResponse;
- OnApplyRevocationResponse = provider.ApplyRevocationResponse;
- OnApplyTokenResponse = provider.ApplyTokenResponse;
- OnApplyUserinfoResponse = provider.ApplyUserinfoResponse;
-
- OnProcessChallengeResponse = provider.ProcessChallengeResponse;
- OnProcessSigninResponse = provider.ProcessSigninResponse;
- OnProcessSignoutResponse = provider.ProcessSignoutResponse;
-
- OnDeserializeAccessToken = provider.DeserializeAccessToken;
- OnDeserializeAuthorizationCode = provider.DeserializeAuthorizationCode;
- OnDeserializeIdentityToken = provider.DeserializeIdentityToken;
- OnDeserializeRefreshToken = provider.DeserializeRefreshToken;
-
- OnSerializeAccessToken = provider.SerializeAccessToken;
- OnSerializeAuthorizationCode = provider.SerializeAuthorizationCode;
- OnSerializeIdentityToken = provider.SerializeIdentityToken;
- OnSerializeRefreshToken = provider.SerializeRefreshToken;
+ return _eventService.PublishAsync(new OpenIddictServerEvents.ProcessSignoutResponse(context));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs
index 2ce9161f..e2749734 100644
--- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs
+++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs
@@ -12,12 +12,12 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
-using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Server;
@@ -48,6 +48,98 @@ namespace Microsoft.Extensions.DependencyInjection
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
+ ///
+ /// Registers an event handler for the specified event type.
+ ///
+ /// The handler added to the DI container.
+ /// The .
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictServerBuilder AddEventHandler(
+ [NotNull] IOpenIddictServerEventHandler handler)
+ where TEvent : class, IOpenIddictServerEvent
+ {
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ Services.AddSingleton(handler);
+
+ return this;
+ }
+
+ ///
+ /// Registers an event handler for the specified event type.
+ ///
+ /// The handler added to the DI container.
+ /// The .
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictServerBuilder AddEventHandler([NotNull] Func handler)
+ where TEvent : class, IOpenIddictServerEvent
+ => AddEventHandler((notification, cancellationToken) => handler(notification));
+
+ ///
+ /// Registers an event handler for the specified event type.
+ ///
+ /// The handler added to the DI container.
+ /// The .
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictServerBuilder AddEventHandler([NotNull] Func handler)
+ where TEvent : class, IOpenIddictServerEvent
+ {
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ return AddEventHandler(new OpenIddictServerEventHandler(handler));
+ }
+
+ ///
+ /// Registers an event handler for the specified event type.
+ ///
+ /// The type of the event.
+ /// The type of the handler.
+ /// The lifetime of the registered service.
+ /// The .
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictServerBuilder AddEventHandler(
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ where TEvent : class, IOpenIddictServerEvent
+ where THandler : IOpenIddictServerEventHandler
+ => AddEventHandler(typeof(THandler));
+
+ ///
+ /// Registers an event handler for the specified event type.
+ ///
+ /// The type of the handler.
+ /// The lifetime of the registered service.
+ /// The .
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictServerBuilder AddEventHandler(
+ [NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ where TEvent : class, IOpenIddictServerEvent
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ if (lifetime == ServiceLifetime.Transient)
+ {
+ throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
+ }
+
+ if (!typeof(IOpenIddictServerEventHandler).IsAssignableFrom(type))
+ {
+ throw new ArgumentException("The specified type is invalid.", nameof(type));
+ }
+
+ Services.Add(new ServiceDescriptor(typeof(IOpenIddictServerEventHandler), type, lifetime));
+
+ return this;
+ }
+
///
/// Amends the default OpenIddict server configuration.
///
@@ -184,7 +276,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (string.IsNullOrEmpty(password))
{
- throw new ArgumentNullException(nameof(password));
+ throw new ArgumentException("The password cannot be null or empty.", nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(assembly, resource, password));
@@ -206,7 +298,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (string.IsNullOrEmpty(password))
{
- throw new ArgumentNullException(nameof(password));
+ throw new ArgumentException("The password cannot be null or empty.", nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(stream, password));
@@ -233,7 +325,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (string.IsNullOrEmpty(password))
{
- throw new ArgumentNullException(nameof(password));
+ throw new ArgumentException("The password cannot be null or empty.", nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(stream, password, flags));
@@ -552,60 +644,6 @@ namespace Microsoft.Extensions.DependencyInjection
return Configure(options => options.Claims.UnionWith(claims));
}
- ///
- /// Registers an application-specific OpenID Connect server provider whose events
- /// are automatically invoked for each request handled by the OpenIddict server handler.
- /// Using this method is NOT recommended if you're not familiar with the OIDC events model.
- ///
- /// The custom service.
- /// The .
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictServerBuilder RegisterProvider([NotNull] OpenIdConnectServerProvider provider)
- {
- if (provider == null)
- {
- throw new ArgumentNullException(nameof(provider));
- }
-
- return Configure(options => options.ApplicationProvider = provider);
- }
-
- ///
- /// Registers an application-specific OpenID Connect server provider whose events
- /// are automatically invoked for each request handled by the OpenIddict server handler.
- /// Using this method is NOT recommended if you're not familiar with the OIDC events model.
- ///
- /// The type of the custom service.
- /// The .
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictServerBuilder RegisterProvider() where TProvider : OpenIdConnectServerProvider
- => RegisterProvider(typeof(TProvider));
-
- ///
- /// Registers an application-specific OpenID Connect server provider whose events
- /// are automatically invoked for each request handled by the OpenIddict server handler.
- /// Using this method is NOT recommended if you're not familiar with the OIDC events model.
- ///
- /// The type of the custom service.
- /// The .
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictServerBuilder RegisterProvider([NotNull] Type type)
- {
- if (type == null)
- {
- throw new ArgumentNullException(nameof(type));
- }
-
- if (!typeof(OpenIdConnectServerProvider).IsAssignableFrom(type))
- {
- throw new ArgumentException("The specified type is invalid.", nameof(type));
- }
-
- Services.TryAddScoped(type);
-
- return Configure(options => options.ApplicationProviderType = type);
- }
-
///
/// Registers the specified scopes as supported scopes so
/// they can be returned as part of the discovery document.
@@ -735,4 +773,4 @@ namespace Microsoft.Extensions.DependencyInjection
public OpenIddictServerBuilder UseRollingTokens()
=> Configure(options => options.UseRollingTokens = true);
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerEvent.cs b/src/OpenIddict.Server/OpenIddictServerEvent.cs
new file mode 100644
index 00000000..bc6d0b34
--- /dev/null
+++ b/src/OpenIddict.Server/OpenIddictServerEvent.cs
@@ -0,0 +1,24 @@
+using System;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Represents an OpenIddict server event.
+ ///
+ /// The type of the context instance associated with the event.
+ public class OpenIddictServerEvent : IOpenIddictServerEvent where TContext : class
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the event.
+ public OpenIddictServerEvent([NotNull] TContext context)
+ => Context = context ?? throw new ArgumentNullException(nameof(context));
+
+ ///
+ /// Gets the context instance associated with the event.
+ ///
+ public TContext Context { get; }
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerEventHandler.cs b/src/OpenIddict.Server/OpenIddictServerEventHandler.cs
new file mode 100644
index 00000000..727353ec
--- /dev/null
+++ b/src/OpenIddict.Server/OpenIddictServerEventHandler.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Represents a handler able to process events.
+ ///
+ /// The type of the events handled by this instance.
+ public class OpenIddictServerEventHandler : IOpenIddictServerEventHandler
+ where TEvent : class, IOpenIddictServerEvent
+ {
+ private readonly Func _handler;
+
+ ///
+ /// Creates a new event using the specified handler delegate.
+ ///
+ /// The event handler delegate
+ public OpenIddictServerEventHandler([NotNull] Func handler)
+ => _handler = handler ?? throw new ArgumentNullException(nameof(handler));
+
+ ///
+ /// Processes the event.
+ ///
+ /// The event to process.
+ ///
+ /// The that can be used to abort the operation.
+ ///
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public Task HandleAsync(TEvent notification, CancellationToken cancellationToken)
+ {
+ if (notification == null)
+ {
+ throw new ArgumentNullException(nameof(notification));
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ return _handler.Invoke(notification, cancellationToken);
+ }
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerEventService.cs b/src/OpenIddict.Server/OpenIddictServerEventService.cs
new file mode 100644
index 00000000..4f359301
--- /dev/null
+++ b/src/OpenIddict.Server/OpenIddictServerEventService.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using AspNet.Security.OpenIdConnect.Primitives;
+using JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
+using static OpenIddict.Server.OpenIddictServerEvents;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Dispatches notifications by invoking the corresponding handlers.
+ ///
+ public class OpenIddictServerEventService : IOpenIddictServerEventService
+ {
+ private readonly IServiceProvider _provider;
+
+ public OpenIddictServerEventService([NotNull] IServiceProvider provider)
+ {
+ _provider = provider;
+ }
+
+ ///
+ /// Publishes a new event.
+ ///
+ /// The type of the event to publish.
+ /// The event to publish.
+ /// The that can be used to abort the operation.
+ /// A that can be used to monitor the asynchronous operation.
+ public async Task PublishAsync([NotNull] TEvent notification, CancellationToken cancellationToken = default)
+ where TEvent : class, IOpenIddictServerEvent
+ {
+ if (notification == null)
+ {
+ throw new ArgumentNullException(nameof(notification));
+ }
+
+ foreach (var handler in _provider.GetServices>())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await handler.HandleAsync(notification, cancellationToken);
+
+ // Note: the following logic determines whether next handlers should be invoked
+ // depending on whether the underlying event context was substantially updated.
+ switch (notification)
+ {
+ case MatchEndpoint value when value.Context.Result != null: return;
+ case MatchEndpoint value when value.Context.IsAuthorizationEndpoint ||
+ value.Context.IsConfigurationEndpoint ||
+ value.Context.IsCryptographyEndpoint ||
+ value.Context.IsIntrospectionEndpoint ||
+ value.Context.IsLogoutEndpoint ||
+ value.Context.IsRevocationEndpoint ||
+ value.Context.IsTokenEndpoint ||
+ value.Context.IsUserinfoEndpoint: return;
+
+ case ExtractAuthorizationRequest value when value.Context.Result != null: return;
+ case ExtractConfigurationRequest value when value.Context.Result != null: return;
+ case ExtractCryptographyRequest value when value.Context.Result != null: return;
+ case ExtractIntrospectionRequest value when value.Context.Result != null: return;
+ case ExtractLogoutRequest value when value.Context.Result != null: return;
+ case ExtractRevocationRequest value when value.Context.Result != null: return;
+ case ExtractTokenRequest value when value.Context.Result != null: return;
+ case ExtractUserinfoRequest value when value.Context.Result != null: return;
+
+ case ValidateAuthorizationRequest value when value.Context.Result != null: return;
+ case ValidateConfigurationRequest value when value.Context.Result != null: return;
+ case ValidateCryptographyRequest value when value.Context.Result != null: return;
+ case ValidateIntrospectionRequest value when value.Context.Result != null: return;
+ case ValidateLogoutRequest value when value.Context.Result != null: return;
+ case ValidateRevocationRequest value when value.Context.Result != null: return;
+ case ValidateTokenRequest value when value.Context.Result != null: return;
+ case ValidateUserinfoRequest value when value.Context.Result != null: return;
+
+ case ValidateAuthorizationRequest value when value.Context.IsRejected: return;
+ case ValidateConfigurationRequest value when value.Context.IsRejected: return;
+ case ValidateCryptographyRequest value when value.Context.IsRejected: return;
+ case ValidateIntrospectionRequest value when value.Context.IsRejected: return;
+ case ValidateLogoutRequest value when value.Context.IsRejected: return;
+ case ValidateRevocationRequest value when value.Context.IsRejected: return;
+ case ValidateTokenRequest value when value.Context.IsRejected: return;
+ case ValidateUserinfoRequest value when value.Context.IsRejected: return;
+
+ case ValidateIntrospectionRequest value when value.Context.IsSkipped: return;
+ case ValidateRevocationRequest value when value.Context.IsSkipped: return;
+ case ValidateTokenRequest value when value.Context.IsSkipped: return;
+
+ case HandleAuthorizationRequest value when value.Context.Result != null: return;
+ case HandleConfigurationRequest value when value.Context.Result != null: return;
+ case HandleCryptographyRequest value when value.Context.Result != null: return;
+ case HandleIntrospectionRequest value when value.Context.Result != null: return;
+ case HandleLogoutRequest value when value.Context.Result != null: return;
+ case HandleRevocationRequest value when value.Context.Result != null: return;
+ case HandleTokenRequest value when value.Context.Result != null: return;
+ case HandleUserinfoRequest value when value.Context.Result != null: return;
+
+ case HandleAuthorizationRequest value when value.Context.Ticket != null: return;
+
+ case HandleTokenRequest value when value.Context.Ticket != null &&
+ !value.Context.Request.IsAuthorizationCodeGrantType() &&
+ !value.Context.Request.IsRefreshTokenGrantType(): return;
+
+ case HandleTokenRequest value when value.Context.Ticket == null &&
+ (value.Context.Request.IsAuthorizationCodeGrantType() ||
+ value.Context.Request.IsRefreshTokenGrantType()): return;
+
+ case HandleAuthorizationRequest value when value.Context.Ticket != null: return;
+
+ case ProcessChallengeResponse value when value.Context.Result != null: return;
+ case ProcessSigninResponse value when value.Context.Result != null: return;
+ case ProcessSignoutResponse value when value.Context.Result != null: return;
+
+ case ProcessChallengeResponse value when value.Context.IsRejected: return;
+ case ProcessSigninResponse value when value.Context.IsRejected: return;
+ case ProcessSignoutResponse value when value.Context.IsRejected: return;
+
+ case ApplyAuthorizationResponse value when value.Context.Result != null: return;
+ case ApplyConfigurationResponse value when value.Context.Result != null: return;
+ case ApplyCryptographyResponse value when value.Context.Result != null: return;
+ case ApplyIntrospectionResponse value when value.Context.Result != null: return;
+ case ApplyLogoutResponse value when value.Context.Result != null: return;
+ case ApplyRevocationResponse value when value.Context.Result != null: return;
+ case ApplyTokenResponse value when value.Context.Result != null: return;
+ case ApplyUserinfoResponse value when value.Context.Result != null: return;
+
+ case DeserializeAuthorizationCode value when value.Context.IsHandled: return;
+ case DeserializeAccessToken value when value.Context.IsHandled: return;
+ case DeserializeIdentityToken value when value.Context.IsHandled: return;
+ case DeserializeRefreshToken value when value.Context.IsHandled: return;
+
+ case DeserializeAuthorizationCode value when value.Context.Ticket != null: return;
+ case DeserializeAccessToken value when value.Context.Ticket != null: return;
+ case DeserializeIdentityToken value when value.Context.Ticket != null: return;
+ case DeserializeRefreshToken value when value.Context.Ticket != null: return;
+
+ case SerializeAuthorizationCode value when value.Context.IsHandled: return;
+ case SerializeAccessToken value when value.Context.IsHandled: return;
+ case SerializeIdentityToken value when value.Context.IsHandled: return;
+ case SerializeRefreshToken value when value.Context.IsHandled: return;
+
+ case SerializeAuthorizationCode value when !string.IsNullOrEmpty(value.Context.AuthorizationCode): return;
+ case SerializeAccessToken value when !string.IsNullOrEmpty(value.Context.AccessToken): return;
+ case SerializeIdentityToken value when !string.IsNullOrEmpty(value.Context.IdentityToken): return;
+ case SerializeRefreshToken value when !string.IsNullOrEmpty(value.Context.RefreshToken): return;
+ }
+ }
+ }
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.cs b/src/OpenIddict.Server/OpenIddictServerEvents.cs
new file mode 100644
index 00000000..d4de707b
--- /dev/null
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.cs
@@ -0,0 +1,565 @@
+using AspNet.Security.OpenIdConnect.Server;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Contains common events used by the OpenIddict server handler.
+ ///
+ public static class OpenIddictServerEvents
+ {
+ ///
+ /// Represents an event called for each HTTP request to determine if
+ /// it should be handled by the OpenID Connect server middleware.
+ ///
+ public sealed class MatchEndpoint : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public MatchEndpoint([NotNull] MatchEndpointContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the authorization endpoint to give the user code
+ /// a chance to manually extract the authorization request from the ambient HTTP context.
+ ///
+ public sealed class ExtractAuthorizationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractAuthorizationRequest([NotNull] ExtractAuthorizationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the configuration endpoint to give the user code
+ /// a chance to manually extract the configuration request from the ambient HTTP context.
+ ///
+ public sealed class ExtractConfigurationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractConfigurationRequest([NotNull] ExtractConfigurationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the cryptography endpoint to give the user code
+ /// a chance to manually extract the cryptography request from the ambient HTTP context.
+ ///
+
+ public sealed class ExtractCryptographyRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractCryptographyRequest([NotNull] ExtractCryptographyRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the introspection endpoint to give the user code
+ /// a chance to manually extract the introspection request from the ambient HTTP context.
+ ///
+ public sealed class ExtractIntrospectionRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the logout endpoint to give the user code
+ /// a chance to manually extract the logout request from the ambient HTTP context.
+ ///
+ public sealed class ExtractLogoutRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractLogoutRequest([NotNull] ExtractLogoutRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the revocation endpoint to give the user code
+ /// a chance to manually extract the revocation request from the ambient HTTP context.
+ ///
+ public sealed class ExtractRevocationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractRevocationRequest([NotNull] ExtractRevocationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the token endpoint to give the user code
+ /// a chance to manually extract the token request from the ambient HTTP context.
+ ///
+ public sealed class ExtractTokenRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractTokenRequest([NotNull] ExtractTokenRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the userinfo endpoint to give the user code
+ /// a chance to manually extract the userinfo request from the ambient HTTP context.
+ ///
+ public sealed class ExtractUserinfoRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ExtractUserinfoRequest([NotNull] ExtractUserinfoRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the authorization endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateAuthorizationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the configuration endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateConfigurationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateConfigurationRequest([NotNull] ValidateConfigurationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the cryptography endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateCryptographyRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateCryptographyRequest([NotNull] ValidateCryptographyRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the introspection endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateIntrospectionRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the logout endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateLogoutRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the revocation endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateRevocationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the token endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateTokenRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateTokenRequest([NotNull] ValidateTokenRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each request to the userinfo endpoint
+ /// to determine if the request is valid and should continue to be processed.
+ ///
+ public sealed class ValidateUserinfoRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateUserinfoRequest([NotNull] ValidateUserinfoRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated authorization request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleAuthorizationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated configuration request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleConfigurationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated cryptography request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleCryptographyRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleCryptographyRequest([NotNull] HandleCryptographyRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated introspection request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleIntrospectionRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated logout request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleLogoutRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleLogoutRequest([NotNull] HandleLogoutRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated revocation request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleRevocationRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleRevocationRequest([NotNull] HandleRevocationRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated token request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleTokenRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleTokenRequest([NotNull] HandleTokenRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called for each validated userinfo request
+ /// to allow the user code to decide how the request should be handled.
+ ///
+ public sealed class HandleUserinfoRequest : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public HandleUserinfoRequest([NotNull] HandleUserinfoRequestContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when processing a challenge response.
+ ///
+ public sealed class ProcessChallengeResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ProcessChallengeResponse([NotNull] ProcessChallengeResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when processing a sign-in response.
+ ///
+ public sealed class ProcessSigninResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ProcessSigninResponse([NotNull] ProcessSigninResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when processing a sign-out response.
+ ///
+ public sealed class ProcessSignoutResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ProcessSignoutResponse([NotNull] ProcessSignoutResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the authorization response is returned to the caller.
+ ///
+ public sealed class ApplyAuthorizationResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the configuration response is returned to the caller.
+ ///
+ public sealed class ApplyConfigurationResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyConfigurationResponse([NotNull] ApplyConfigurationResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the cryptography response is returned to the caller.
+ ///
+ public sealed class ApplyCryptographyResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyCryptographyResponse([NotNull] ApplyCryptographyResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the introspection response is returned to the caller.
+ ///
+ public sealed class ApplyIntrospectionResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyIntrospectionResponse([NotNull] ApplyIntrospectionResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the logout response is returned to the caller.
+ ///
+ public sealed class ApplyLogoutResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the revocation response is returned to the caller.
+ ///
+ public sealed class ApplyRevocationResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyRevocationResponse([NotNull] ApplyRevocationResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the token response is returned to the caller.
+ ///
+ public sealed class ApplyTokenResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyTokenResponse([NotNull] ApplyTokenResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called before the userinfo response is returned to the caller.
+ ///
+ public sealed class ApplyUserinfoResponse : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyUserinfoResponse([NotNull] ApplyUserinfoResponseContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when serializing an authorization code.
+ ///
+ public sealed class SerializeAuthorizationCode : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when serializing an access token.
+ ///
+ public sealed class SerializeAccessToken : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public SerializeAccessToken([NotNull] SerializeAccessTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when serializing an identity token.
+ ///
+ public sealed class SerializeIdentityToken : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public SerializeIdentityToken([NotNull] SerializeIdentityTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when serializing a refresh token.
+ ///
+ public sealed class SerializeRefreshToken : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when deserializing an authorization code.
+ ///
+ public sealed class DeserializeAuthorizationCode : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public DeserializeAuthorizationCode([NotNull] DeserializeAuthorizationCodeContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when deserializing an access token.
+ ///
+ public sealed class DeserializeAccessToken : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public DeserializeAccessToken([NotNull] DeserializeAccessTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when deserializing an identity token.
+ ///
+ public sealed class DeserializeIdentityToken : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public DeserializeIdentityToken([NotNull] DeserializeIdentityTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Represents an event called when deserializing a refresh token.
+ ///
+ public sealed class DeserializeRefreshToken : OpenIddictServerEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public DeserializeRefreshToken([NotNull] DeserializeRefreshTokenContext context) : base(context) { }
+ }
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
index 28265df7..9a13deb2 100644
--- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
@@ -34,6 +34,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddAuthentication();
+ builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
builder.Services.TryAddScoped(provider =>
{
@@ -44,6 +45,7 @@ namespace Microsoft.Extensions.DependencyInjection
return new OpenIddictServerProvider(
provider.GetRequiredService>(),
+ provider.GetRequiredService(),
provider.GetService() ?? throw CreateException(),
provider.GetService() ?? throw CreateException(),
provider.GetService() ?? throw CreateException(),
diff --git a/src/OpenIddict.Server/OpenIddictServerOptions.cs b/src/OpenIddict.Server/OpenIddictServerOptions.cs
index bfa7c6a6..e39eed5f 100644
--- a/src/OpenIddict.Server/OpenIddictServerOptions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerOptions.cs
@@ -34,20 +34,6 @@ namespace OpenIddict.Server
///
public bool AcceptAnonymousClients { get; set; }
- ///
- /// Gets or sets the user-provided that the OpenIddict server
- /// invokes to enable developer control over the entire authentication/authorization process.
- ///
- public OpenIdConnectServerProvider ApplicationProvider { get; set; }
-
- ///
- /// Gets or sets the user-provided provider type that the OpenIddict server handler instantiates
- /// to enable developer control over the entire authentication/authorization process. When this
- /// property is set, the provider is resolved from the services container. If the provider is not
- /// guaranteed to be thread-safe, registering it as a scoped dependency is strongly recommended.
- ///
- public Type ApplicationProviderType { get; set; }
-
///
/// Gets or sets the distributed cache used by OpenIddict. If no cache is explicitly
/// provided, the cache registered in the dependency injection container is used.
diff --git a/src/OpenIddict.Validation/IOpenIddictValidationEvent.cs b/src/OpenIddict.Validation/IOpenIddictValidationEvent.cs
new file mode 100644
index 00000000..4e8bc57a
--- /dev/null
+++ b/src/OpenIddict.Validation/IOpenIddictValidationEvent.cs
@@ -0,0 +1,7 @@
+namespace OpenIddict.Validation
+{
+ ///
+ /// Represents an OpenIddict validation event.
+ ///
+ public interface IOpenIddictValidationEvent { }
+}
diff --git a/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs b/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
new file mode 100644
index 00000000..6bb304c5
--- /dev/null
+++ b/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
@@ -0,0 +1,25 @@
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Represents a handler able to process events.
+ ///
+ /// The type of the events handled by this instance.
+ public interface IOpenIddictValidationEventHandler where TEvent : class, IOpenIddictValidationEvent
+ {
+ ///
+ /// Processes the event.
+ ///
+ /// The event to process.
+ ///
+ /// The that can be used to abort the operation.
+ ///
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
+ }
+}
diff --git a/src/OpenIddict.Validation/IOpenIddictValidationEventService.cs b/src/OpenIddict.Validation/IOpenIddictValidationEventService.cs
new file mode 100644
index 00000000..06ea4fd8
--- /dev/null
+++ b/src/OpenIddict.Validation/IOpenIddictValidationEventService.cs
@@ -0,0 +1,22 @@
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Dispatches events by invoking the corresponding handlers.
+ ///
+ public interface IOpenIddictValidationEventService
+ {
+ ///
+ /// Publishes a new event.
+ ///
+ /// The type of the event to publish.
+ /// The event to publish.
+ /// The that can be used to abort the operation.
+ /// A that can be used to monitor the asynchronous operation.
+ Task PublishAsync([NotNull] TEvent notification, CancellationToken cancellationToken = default)
+ where TEvent : class, IOpenIddictValidationEvent;
+ }
+}
diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationHandler.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationHandler.cs
index a14f3cd4..ed0b0982 100644
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationHandler.cs
+++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationHandler.cs
@@ -4,11 +4,8 @@
* the license and the contributors participating to this project.
*/
-using System;
using System.ComponentModel;
-using System.Text;
using System.Text.Encodings.Web;
-using System.Threading.Tasks;
using AspNet.Security.OAuth.Validation;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
@@ -28,36 +25,5 @@ namespace OpenIddict.Validation
: base(options, logger, encoder, clock)
{
}
-
- protected override async Task InitializeEventsAsync()
- {
- await base.InitializeEventsAsync();
-
- // If an application provider instance or type was specified, import the application provider events.
- if (Options.ApplicationEvents != null || Options.ApplicationEventsType != null)
- {
- // Resolve the user provider from the options or from the services container.
- var events = Options.ApplicationEvents;
- if (events == null)
- {
- events = Context.RequestServices.GetService(Options.ApplicationEventsType) as OAuthValidationEvents;
- }
-
- if (events == null)
- {
- throw new InvalidOperationException(new StringBuilder()
- .AppendLine("The application events cannot be resolved from the dependency injection container. ")
- .Append("Make sure they are correctly registered in 'ConfigureServices(IServiceCollection services)'.")
- .ToString());
- }
-
- // Update the main events to invoke the user provider's event handlers.
- Events.Import(events);
- }
- }
-
- private new OpenIddictValidationEvents Events => (OpenIddictValidationEvents) base.Events;
-
- private new OpenIddictValidationOptions Options => (OpenIddictValidationOptions) base.Options;
}
}
diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs
index 1da101f6..ab3e5b2c 100644
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs
+++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs
@@ -6,7 +6,6 @@
using System;
using System.ComponentModel;
-using AspNet.Security.OAuth.Validation;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.DataProtection;
@@ -49,19 +48,6 @@ namespace OpenIddict.Validation
throw new ArgumentException("The options instance name cannot be null or empty.", nameof(name));
}
- if (options.ApplicationEventsType != null)
- {
- if (options.ApplicationEvents != null)
- {
- throw new InvalidOperationException("Application events cannot be registered when a type is specified.");
- }
-
- if (!typeof(OAuthValidationEvents).IsAssignableFrom(options.ApplicationEventsType))
- {
- throw new InvalidOperationException("Application events must inherit from OAuthValidationEvents.");
- }
- }
-
if (options.DataProtectionProvider == null)
{
options.DataProtectionProvider = _dataProtectionProvider;
diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationEvents.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
similarity index 77%
rename from src/OpenIddict.Validation/Internal/OpenIddictValidationEvents.cs
rename to src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
index 7190ec13..ccfa8172 100644
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationEvents.cs
+++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
@@ -19,8 +19,19 @@ namespace OpenIddict.Validation
/// Provides the logic necessary to extract, validate and handle OAuth2 requests.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public class OpenIddictValidationEvents : OAuthValidationEvents
+ public class OpenIddictValidationProvider : OAuthValidationEvents
{
+ private readonly IOpenIddictValidationEventService _eventService;
+
+ public OpenIddictValidationProvider([NotNull] IOpenIddictValidationEventService eventService)
+ => _eventService = eventService;
+
+ public override Task ApplyChallenge([NotNull] ApplyChallengeContext context)
+ => _eventService.PublishAsync(new OpenIddictValidationEvents.ApplyChallenge(context));
+
+ public override Task CreateTicket([NotNull] CreateTicketContext context)
+ => _eventService.PublishAsync(new OpenIddictValidationEvents.CreateTicket(context));
+
public override async Task DecryptToken([NotNull] DecryptTokenContext context)
{
var options = (OpenIddictValidationOptions) context.Options;
@@ -80,16 +91,13 @@ namespace OpenIddict.Validation
context.Success();
}
- await base.DecryptToken(context);
+ await _eventService.PublishAsync(new OpenIddictValidationEvents.DecryptToken(context));
}
- public void Import([NotNull] OAuthValidationEvents events)
- {
- OnApplyChallenge = events.ApplyChallenge;
- OnCreateTicket = events.CreateTicket;
- OnDecryptToken = events.DecryptToken;
- OnRetrieveToken = events.RetrieveToken;
- OnValidateToken = events.ValidateToken;
- }
+ public override Task RetrieveToken([NotNull] RetrieveTokenContext context)
+ => _eventService.PublishAsync(new OpenIddictValidationEvents.RetrieveToken(context));
+
+ public override Task ValidateToken([NotNull] ValidateTokenContext context)
+ => _eventService.PublishAsync(new OpenIddictValidationEvents.ValidateToken(context));
}
}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
index c29a7f7c..4f1841a0 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
@@ -7,10 +7,11 @@
using System;
using System.ComponentModel;
using System.Linq;
-using AspNet.Security.OAuth.Validation;
+using System.Threading;
+using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
+using OpenIddict.Abstractions;
using OpenIddict.Validation;
namespace Microsoft.Extensions.DependencyInjection
@@ -41,93 +42,134 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
///
- /// Amends the default OpenIddict validation configuration.
+ /// Registers an event handler for the specified event type.
///
- /// The delegate used to configure the OpenIddict options.
- /// This extension can be safely called multiple times.
+ /// The handler added to the DI container.
/// The .
- public OpenIddictValidationBuilder Configure([NotNull] Action configuration)
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictValidationBuilder AddEventHandler(
+ [NotNull] IOpenIddictValidationEventHandler handler)
+ where TEvent : class, IOpenIddictValidationEvent
{
- if (configuration == null)
+ if (handler == null)
{
- throw new ArgumentNullException(nameof(configuration));
+ throw new ArgumentNullException(nameof(handler));
}
- Services.Configure(OpenIddictValidationDefaults.AuthenticationScheme, configuration);
+ Services.AddSingleton(handler);
return this;
}
///
- /// Registers the specified values as valid audiences. Setting the audiences is recommended
- /// when the authorization server issues access tokens for multiple distinct resource servers.
+ /// Registers an event handler for the specified event type.
///
- /// The audiences valid for this resource server.
+ /// The handler added to the DI container.
/// The .
- public OpenIddictValidationBuilder AddAudiences([NotNull] params string[] audiences)
- {
- if (audiences == null)
- {
- throw new ArgumentNullException(nameof(audiences));
- }
-
- if (audiences.Any(audience => string.IsNullOrEmpty(audience)))
- {
- throw new ArgumentException("Audiences cannot be null or empty.", nameof(audiences));
- }
-
- return Configure(options => options.Audiences.UnionWith(audiences));
- }
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public OpenIddictValidationBuilder AddEventHandler([NotNull] Func handler)
+ where TEvent : class, IOpenIddictValidationEvent
+ => AddEventHandler((notification, cancellationToken) => handler(notification));
///
- /// Registers application-specific OAuth2 validation events that are automatically
- /// invoked for each request handled by the OpenIddict validation handler.
+ /// Registers an event handler for the specified event type.
///
- /// The custom service.
- /// The .
+ /// The handler added to the DI container.
+ /// The .
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictValidationBuilder RegisterEvents([NotNull] OAuthValidationEvents events)
+ public OpenIddictValidationBuilder AddEventHandler([NotNull] Func handler)
+ where TEvent : class, IOpenIddictValidationEvent
{
- if (events == null)
+ if (handler == null)
{
- throw new ArgumentNullException(nameof(events));
+ throw new ArgumentNullException(nameof(handler));
}
- return Configure(options => options.ApplicationEvents = events);
+ return AddEventHandler(new OpenIddictValidationEventHandler(handler));
}
///
- /// Registers application-specific OAuth2 validation events that are automatically
- /// invoked for each request handled by the OpenIddict validation handler.
+ /// Registers an event handler for the specified event type.
///
- /// The type of the custom service.
+ /// The type of the event.
+ /// The type of the handler.
+ /// The lifetime of the registered service.
/// The .
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictValidationBuilder RegisterEvents() where TEvents : OAuthValidationEvents
- => RegisterEvents(typeof(TEvents));
+ public OpenIddictValidationBuilder AddEventHandler(
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ where TEvent : class, IOpenIddictValidationEvent
+ where THandler : IOpenIddictValidationEventHandler
+ => AddEventHandler(typeof(THandler));
///
- /// Registers application-specific OAuth2 validation events that are automatically
- /// invoked for each request handled by the OpenIddict validation handler.
+ /// Registers an event handler for the specified event type.
///
- /// The type of the custom service.
+ /// The type of the handler.
+ /// The lifetime of the registered service.
/// The .
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictValidationBuilder RegisterEvents([NotNull] Type type)
+ public OpenIddictValidationBuilder AddEventHandler(
+ [NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ where TEvent : class, IOpenIddictValidationEvent
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
- if (!typeof(OAuthValidationEvents).IsAssignableFrom(type))
+ if (lifetime == ServiceLifetime.Transient)
+ {
+ throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
+ }
+
+ if (!typeof(IOpenIddictValidationEventHandler).IsAssignableFrom(type))
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
- Services.TryAddScoped(type);
+ Services.Add(new ServiceDescriptor(typeof(IOpenIddictValidationEventHandler), type, lifetime));
+
+ return this;
+ }
+
+ ///
+ /// Amends the default OpenIddict validation configuration.
+ ///
+ /// The delegate used to configure the OpenIddict options.
+ /// This extension can be safely called multiple times.
+ /// The .
+ public OpenIddictValidationBuilder Configure([NotNull] Action configuration)
+ {
+ if (configuration == null)
+ {
+ throw new ArgumentNullException(nameof(configuration));
+ }
+
+ Services.Configure(OpenIddictValidationDefaults.AuthenticationScheme, configuration);
- return Configure(options => options.ApplicationEventsType = type);
+ return this;
+ }
+
+ ///
+ /// Registers the specified values as valid audiences. Setting the audiences is recommended
+ /// when the authorization server issues access tokens for multiple distinct resource servers.
+ ///
+ /// The audiences valid for this resource server.
+ /// The .
+ public OpenIddictValidationBuilder AddAudiences([NotNull] params string[] audiences)
+ {
+ if (audiences == null)
+ {
+ throw new ArgumentNullException(nameof(audiences));
+ }
+
+ if (audiences.Any(audience => string.IsNullOrEmpty(audience)))
+ {
+ throw new ArgumentException("Audiences cannot be null or empty.", nameof(audiences));
+ }
+
+ return Configure(options => options.Audiences.UnionWith(audiences));
}
///
@@ -176,4 +218,4 @@ namespace Microsoft.Extensions.DependencyInjection
public OpenIddictValidationBuilder UseReferenceTokens()
=> Configure(options => options.UseReferenceTokens = true);
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEvent.cs b/src/OpenIddict.Validation/OpenIddictValidationEvent.cs
new file mode 100644
index 00000000..ec75eeb6
--- /dev/null
+++ b/src/OpenIddict.Validation/OpenIddictValidationEvent.cs
@@ -0,0 +1,24 @@
+using System;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Represents an OpenIddict validation event.
+ ///
+ /// The type of the context instance associated with the event.
+ public class OpenIddictValidationEvent : IOpenIddictValidationEvent where TContext : class
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the event.
+ public OpenIddictValidationEvent([NotNull] TContext context)
+ => Context = context ?? throw new ArgumentNullException(nameof(context));
+
+ ///
+ /// Gets the context instance associated with the event.
+ ///
+ public TContext Context { get; }
+ }
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs b/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
new file mode 100644
index 00000000..c40704ca
--- /dev/null
+++ b/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Represents a handler able to process events.
+ ///
+ /// The type of the events handled by this instance.
+ public class OpenIddictValidationEventHandler : IOpenIddictValidationEventHandler
+ where TEvent : class, IOpenIddictValidationEvent
+ {
+ private readonly Func _handler;
+
+ ///
+ /// Creates a new event using the specified handler delegate.
+ ///
+ /// The event handler delegate
+ public OpenIddictValidationEventHandler([NotNull] Func handler)
+ => _handler = handler ?? throw new ArgumentNullException(nameof(handler));
+
+ ///
+ /// Processes the event.
+ ///
+ /// The event to process.
+ ///
+ /// The that can be used to abort the operation.
+ ///
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public Task HandleAsync(TEvent notification, CancellationToken cancellationToken)
+ {
+ if (notification == null)
+ {
+ throw new ArgumentNullException(nameof(notification));
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+
+ return _handler.Invoke(notification, cancellationToken);
+ }
+ }
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEventService.cs b/src/OpenIddict.Validation/OpenIddictValidationEventService.cs
new file mode 100644
index 00000000..451b8835
--- /dev/null
+++ b/src/OpenIddict.Validation/OpenIddictValidationEventService.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Dispatches notifications by invoking the corresponding handlers.
+ ///
+ public class OpenIddictValidationEventService : IOpenIddictValidationEventService
+ {
+ private readonly IServiceProvider _provider;
+
+ public OpenIddictValidationEventService([NotNull] IServiceProvider provider)
+ {
+ _provider = provider;
+ }
+
+ ///
+ /// Publishes a new event.
+ ///
+ /// The type of the event to publish.
+ /// The event to publish.
+ /// The that can be used to abort the operation.
+ /// A that can be used to monitor the asynchronous operation.
+ public async Task PublishAsync([NotNull] TEvent notification, CancellationToken cancellationToken = default)
+ where TEvent : class, IOpenIddictValidationEvent
+ {
+ if (notification == null)
+ {
+ throw new ArgumentNullException(nameof(notification));
+ }
+
+ foreach (var handler in _provider.GetServices>())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await handler.HandleAsync(notification, cancellationToken);
+
+ // Note: the following logic determines whether next handlers should be invoked
+ // depending on whether the underlying event context was substantially updated.
+ switch (notification)
+ {
+ case OpenIddictValidationEvents.ApplyChallenge value when value.Context.Handled: return;
+
+ case OpenIddictValidationEvents.CreateTicket value when value.Context.Result != null: return;
+ case OpenIddictValidationEvents.CreateTicket value when value.Context.Principal == null: return;
+
+ case OpenIddictValidationEvents.DecryptToken value when value.Context.Result != null: return;
+ case OpenIddictValidationEvents.DecryptToken value when value.Context.Principal != null: return;
+
+ case OpenIddictValidationEvents.RetrieveToken value when value.Context.Result != null: return;
+ case OpenIddictValidationEvents.RetrieveToken value when value.Context.Principal != null: return;
+
+ case OpenIddictValidationEvents.ValidateToken value when value.Context.Result != null: return;
+ case OpenIddictValidationEvents.ValidateToken value when value.Context.Principal == null: return;
+ }
+ }
+ }
+ }
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEvents.cs b/src/OpenIddict.Validation/OpenIddictValidationEvents.cs
new file mode 100644
index 00000000..333bb7bd
--- /dev/null
+++ b/src/OpenIddict.Validation/OpenIddictValidationEvents.cs
@@ -0,0 +1,71 @@
+using AspNet.Security.OAuth.Validation;
+using JetBrains.Annotations;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Contains common events used by the OpenIddict validation handler.
+ ///
+ public static class OpenIddictValidationEvents
+ {
+ ///
+ /// Invoked when a challenge response is returned to the caller.
+ ///
+ public sealed class ApplyChallenge : OpenIddictValidationEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ApplyChallenge([NotNull] ApplyChallengeContext context) : base(context) { }
+ }
+
+ ///
+ /// Invoked when a ticket is to be created from an introspection response.
+ ///
+ public sealed class CreateTicket : OpenIddictValidationEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public CreateTicket([NotNull] CreateTicketContext context) : base(context) { }
+ }
+
+ ///
+ /// Invoked when a token is to be decrypted.
+ ///
+ public sealed class DecryptToken : OpenIddictValidationEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public DecryptToken([NotNull] DecryptTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Invoked when a token is to be parsed from a newly-received request.
+ ///
+ public sealed class RetrieveToken : OpenIddictValidationEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public RetrieveToken([NotNull] RetrieveTokenContext context) : base(context) { }
+ }
+
+ ///
+ /// Invoked when a token is to be validated, before final processing.
+ ///
+ public sealed class ValidateToken : OpenIddictValidationEvent
+ {
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The context instance associated with the notification.
+ public ValidateToken([NotNull] ValidateTokenContext context) : base(context) { }
+ }
+ }
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
index c0b8ee95..438f6ad6 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
@@ -34,8 +34,9 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddAuthentication();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
+ builder.Services.TryAddScoped();
// Note: TryAddEnumerable() is used here to ensure the initializer is only registered once.
builder.Services.TryAddEnumerable(new[]
diff --git a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs
index 43bccdf7..869ac26c 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationOptions.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationOptions.cs
@@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
-using System;
using AspNet.Security.OAuth.Validation;
namespace OpenIddict.Validation
@@ -20,23 +19,9 @@ namespace OpenIddict.Validation
public OpenIddictValidationOptions()
{
Events = null;
- EventsType = typeof(OpenIddictValidationEvents);
+ EventsType = typeof(OpenIddictValidationProvider);
}
- ///
- /// Gets or sets the user-provided that the OpenIddict
- /// validation handler invokes to enable developer control over the entire authentication process.
- ///
- public OAuthValidationEvents ApplicationEvents { get; set; }
-
- ///
- /// Gets or sets the user-provided provider type that the OpenIddict validation handler
- /// instantiates to enable developer control over the entire authentication process. When this
- /// property is set, the provider is resolved from the services container. If the provider is not
- /// guaranteed to be thread-safe, registering it as a scoped dependency is strongly recommended.
- ///
- public Type ApplicationEventsType { get; set; }
-
///
/// Gets or sets a boolean indicating whether reference tokens are used.
///
diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs
index 3ed140aa..72eacfbd 100644
--- a/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs
+++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs
@@ -9,7 +9,6 @@ using System.Text;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Client;
using AspNet.Security.OpenIdConnect.Primitives;
-using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -44,52 +43,6 @@ namespace OpenIddict.Server.Tests
Assert.Equal("A random number generator must be registered.", exception.Message);
}
- [Fact]
- public async Task PostConfigure_ThrowsAnExceptionWhenApplicationProviderTypeAndInstanceAreProvided()
- {
- // Arrange
- var server = CreateAuthorizationServer(builder =>
- {
- builder.Configure(options =>
- {
- options.ApplicationProvider = new OpenIdConnectServerProvider();
- options.ApplicationProviderType = typeof(OpenIdConnectServerProvider);
- });
- });
-
- var client = new OpenIdConnectClient(server.CreateClient());
-
- // Act and assert
- var exception = await Assert.ThrowsAsync(delegate
- {
- return client.GetAsync("/");
- });
-
- // Assert
- Assert.Equal("An application provider cannot be registered when a type is specified.", exception.Message);
- }
-
- [Fact]
- public async Task PostConfigure_ThrowsAnExceptionForInvalidApplicationProviderType()
- {
- // Arrange
- var server = CreateAuthorizationServer(builder =>
- {
- builder.Configure(options => options.ApplicationProviderType = typeof(object));
- });
-
- var client = new OpenIdConnectClient(server.CreateClient());
-
- // Act and assert
- var exception = await Assert.ThrowsAsync(delegate
- {
- return client.GetAsync("/");
- });
-
- // Assert
- Assert.Equal("Application providers must inherit from OpenIdConnectServerProvider.", exception.Message);
- }
-
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenNoFlowIsEnabled()
{
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
index e93abfd8..752c4913 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
@@ -7,8 +7,9 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
-using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@@ -16,11 +17,63 @@ using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Moq;
using Xunit;
+using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server.Tests
{
public class OpenIddictServerBuilderTests
{
+ [Fact]
+ public void AddEventHandler_HandlerIsAttached()
+ {
+ // Arrange
+ var services = CreateServices();
+ var builder = CreateBuilder(services);
+ var handler = new OpenIddictServerEventHandler(
+ (notification, cancellationToken) => Task.CompletedTask);
+
+ // Act
+ builder.AddEventHandler(handler);
+
+ // Assert
+ Assert.Contains(services, service =>
+ service.ServiceType == typeof(IOpenIddictServerEventHandler) &&
+ service.ImplementationInstance == handler);
+ }
+
+ [Fact]
+ public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
+ {
+ // Arrange
+ var services = CreateServices();
+ var builder = CreateBuilder(services);
+
+ // Act and assert
+ var exception = Assert.Throws(delegate
+ {
+ return builder.AddEventHandler(typeof(object));
+ });
+
+ Assert.Equal("type", exception.ParamName);
+ Assert.StartsWith("The specified type is invalid.", exception.Message);
+ }
+
+ [Fact]
+ public void AddEventHandler_HandlerIsRegistered()
+ {
+ // Arrange
+ var services = CreateServices();
+ var builder = CreateBuilder(services);
+
+ // Act
+ builder.AddEventHandler();
+
+ // Assert
+ Assert.Contains(services, service =>
+ service.ServiceType == typeof(IOpenIddictServerEventHandler) &&
+ service.ImplementationType == typeof(CustomHandler));
+ }
+
[Fact]
public void Configure_OptionsAreCorrectlyAmended()
{
@@ -600,69 +653,6 @@ namespace OpenIddict.Server.Tests
Assert.Equal(new Uri("http://www.fabrikam.com/"), options.Issuer);
}
- [Fact]
- public void RegisterProvider_ProviderIsAttached()
- {
- // Arrange
- var services = CreateServices();
- var builder = CreateBuilder(services);
-
- // Act
- builder.RegisterProvider(new OpenIdConnectServerProvider());
-
- var options = GetOptions(services);
-
- // Assert
- Assert.NotNull(options.ApplicationProvider);
- }
-
- [Fact]
- public void RegisterProvider_ThrowsAnExceptionForInvalidProviderType()
- {
- // Arrange
- var services = CreateServices();
- var builder = CreateBuilder(services);
-
- // Act and assert
- var exception = Assert.Throws(delegate
- {
- return builder.RegisterProvider(typeof(object));
- });
-
- Assert.Equal("type", exception.ParamName);
- Assert.StartsWith("The specified type is invalid.", exception.Message);
- }
-
- [Fact]
- public void RegisterProvider_ProviderTypeIsAttached()
- {
- // Arrange
- var services = CreateServices();
- var builder = CreateBuilder(services);
-
- // Act
- builder.RegisterProvider(typeof(OpenIdConnectServerProvider));
-
- var options = GetOptions(services);
-
- // Assert
- Assert.Equal(typeof(OpenIdConnectServerProvider), options.ApplicationProviderType);
- }
-
- [Fact]
- public void RegisterProvider_ProviderIsRegistered()
- {
- // Arrange
- var services = CreateServices();
- var builder = CreateBuilder(services);
-
- // Act
- builder.RegisterProvider(typeof(OpenIdConnectServerProvider));
-
- // Assert
- Assert.Contains(services, service => service.ServiceType == typeof(OpenIdConnectServerProvider));
- }
-
[Fact]
public void RegisterClaims_ClaimsAreAdded()
{
@@ -757,5 +747,12 @@ namespace OpenIddict.Server.Tests
var options = provider.GetRequiredService>();
return options.Get(OpenIddictServerDefaults.AuthenticationScheme);
}
+
+ public class CustomHandler : OpenIddictServerEventHandler
+ {
+ public CustomHandler(Func handler) : base(handler)
+ {
+ }
+ }
}
}
diff --git a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs
deleted file mode 100644
index 44b815d1..00000000
--- a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs
+++ /dev/null
@@ -1,110 +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;
-using System.Threading.Tasks;
-using AspNet.Security.OAuth.Validation;
-using AspNet.Security.OpenIdConnect.Client;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.TestHost;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Xunit;
-
-namespace OpenIddict.Validation.Tests
-{
- public class OpenIddictValidationInitializerTests
- {
- [Fact]
- public async Task PostConfigure_ThrowsAnExceptionWhenApplicationEventsTypeAndInstanceAreProvided()
- {
- // Arrange
- var server = CreateAuthorizationServer(builder =>
- {
- builder.Configure(options =>
- {
- options.ApplicationEvents = new OAuthValidationEvents();
- options.ApplicationEventsType = typeof(OAuthValidationEvents);
- });
- });
-
- var client = new OpenIdConnectClient(server.CreateClient());
-
- // Act and assert
- var exception = await Assert.ThrowsAsync(delegate
- {
- return client.GetAsync("/");
- });
-
- // Assert
- Assert.Equal("Application events cannot be registered when a type is specified.", exception.Message);
- }
-
- [Fact]
- public async Task PostConfigure_ThrowsAnExceptionForInvalidApplicationEventsType()
- {
- // Arrange
- var server = CreateAuthorizationServer(builder =>
- {
- builder.Configure(options => options.ApplicationEventsType = typeof(object));
- });
-
- var client = new OpenIdConnectClient(server.CreateClient());
-
- // Act and assert
- var exception = await Assert.ThrowsAsync(delegate
- {
- return client.GetAsync("/");
- });
-
- // Assert
- Assert.Equal("Application events must inherit from OAuthValidationEvents.", exception.Message);
- }
-
- private static TestServer CreateAuthorizationServer(Action configuration = null)
- {
- var builder = new WebHostBuilder();
-
- builder.UseEnvironment("Testing");
-
- builder.ConfigureLogging(options => options.AddDebug());
-
- builder.ConfigureServices(services =>
- {
- services.AddAuthentication();
- services.AddOptions();
- services.AddDistributedMemoryCache();
-
- services.AddOpenIddict()
- .AddCore(options =>
- {
- options.SetDefaultApplicationEntity()
- .SetDefaultAuthorizationEntity()
- .SetDefaultScopeEntity()
- .SetDefaultTokenEntity();
- })
-
- .AddValidation(options => configuration?.Invoke(options));
- });
-
- builder.Configure(app =>
- {
- app.UseAuthentication();
-
- app.Run(context => context.ChallengeAsync(OpenIddictValidationDefaults.AuthenticationScheme));
- });
-
- return new TestServer(builder);
- }
-
- public class OpenIddictApplication { }
- public class OpenIddictAuthorization { }
- public class OpenIddictScope { }
- public class OpenIddictToken { }
- }
-}
diff --git a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationEventsTests.cs b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationProviderTests.cs
similarity index 99%
rename from test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationEventsTests.cs
rename to test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationProviderTests.cs
index 41d83e7e..77fc072d 100644
--- a/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationEventsTests.cs
+++ b/test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationProviderTests.cs
@@ -34,7 +34,7 @@ using Xunit;
namespace OpenIddict.Validation.Tests
{
- public class OpenIddictValidationEventsTests
+ public class OpenIddictValidationProviderTests
{
[Fact]
public async Task DecryptToken_ThrowsAnExceptionWhenTokenManagerIsNotRegistered()
diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
index d1750617..27648534 100644
--- a/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
@@ -5,109 +5,99 @@
*/
using System;
-using AspNet.Security.OAuth.Validation;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
+using static OpenIddict.Validation.OpenIddictValidationEvents;
namespace OpenIddict.Validation.Tests
{
public class OpenIddictValidationBuilderTests
{
[Fact]
- public void Configure_OptionsAreCorrectlyAmended()
+ public void AddEventHandler_HandlerIsAttached()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
+ var handler = new OpenIddictValidationEventHandler(
+ (notification, cancellationToken) => Task.CompletedTask);
// Act
- builder.Configure(configuration => configuration.ClaimsIssuer = "custom_issuer");
-
- var options = GetOptions(services);
+ builder.AddEventHandler(handler);
// Assert
- Assert.Equal("custom_issuer", options.ClaimsIssuer);
+ Assert.Contains(services, service =>
+ service.ServiceType == typeof(IOpenIddictValidationEventHandler) &&
+ service.ImplementationInstance == handler);
}
[Fact]
- public void AddAudiences_AudiencesAreAdded()
+ public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
- // Act
- builder.AddAudiences("Fabrikam", "Contoso");
-
- var options = GetOptions(services);
+ // Act and assert
+ var exception = Assert.Throws(delegate
+ {
+ return builder.AddEventHandler(typeof(object));
+ });
- // Assert
- Assert.Equal(new[] { "Fabrikam", "Contoso" }, options.Audiences);
+ Assert.Equal("type", exception.ParamName);
+ Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
- public void RegisterEvents_EventsAreAttached()
+ public void AddEventHandler_HandlerIsRegistered()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
- builder.RegisterEvents(new OAuthValidationEvents());
-
- var options = GetOptions(services);
+ builder.AddEventHandler();
// Assert
- Assert.NotNull(options.ApplicationEvents);
+ Assert.Contains(services, service =>
+ service.ServiceType == typeof(IOpenIddictValidationEventHandler) &&
+ service.ImplementationType == typeof(CustomHandler));
}
[Fact]
- public void RegisterEvents_ThrowsAnExceptionForInvalidEventsType()
- {
- // Arrange
- var services = CreateServices();
- var builder = CreateBuilder(services);
-
- // Act and assert
- var exception = Assert.Throws(delegate
- {
- return builder.RegisterEvents(typeof(object));
- });
-
- Assert.Equal("type", exception.ParamName);
- Assert.StartsWith("The specified type is invalid.", exception.Message);
- }
-
- [Fact]
- public void RegisterEvents_EventsTypeIsAttached()
+ public void Configure_OptionsAreCorrectlyAmended()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
- builder.RegisterEvents(typeof(OAuthValidationEvents));
+ builder.Configure(configuration => configuration.ClaimsIssuer = "custom_issuer");
var options = GetOptions(services);
// Assert
- Assert.Equal(typeof(OAuthValidationEvents), options.ApplicationEventsType);
+ Assert.Equal("custom_issuer", options.ClaimsIssuer);
}
[Fact]
- public void RegisterEvents_EventsAreRegistered()
+ public void AddAudiences_AudiencesAreAdded()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
- builder.RegisterEvents(typeof(OAuthValidationEvents));
+ builder.AddAudiences("Fabrikam", "Contoso");
+
+ var options = GetOptions(services);
// Assert
- Assert.Contains(services, service => service.ServiceType == typeof(OAuthValidationEvents));
+ Assert.Equal(new[] { "Fabrikam", "Contoso" }, options.Audiences);
}
[Fact]
@@ -186,5 +176,12 @@ namespace OpenIddict.Validation.Tests
var options = provider.GetRequiredService>();
return options.Get(OpenIddictValidationDefaults.AuthenticationScheme);
}
+
+ public class CustomHandler : OpenIddictValidationEventHandler
+ {
+ public CustomHandler(Func handler) : base(handler)
+ {
+ }
+ }
}
}