diff --git a/OpenIddict.sln b/OpenIddict.sln
index bfaef2b9..5a8a75af 100644
--- a/OpenIddict.sln
+++ b/OpenIddict.sln
@@ -61,7 +61,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.MongoDb.Models",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Abstractions.Tests", "test\OpenIddict.Abstractions.Tests\OpenIddict.Abstractions.Tests.csproj", "{8FACE85E-EF8F-4AB1-85DD-4010D5E2165D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.MongoDb.Tests", "test\OpenIddict.MongoDb.Tests\OpenIddict.MongoDb.Tests.csproj", "{27F603EF-D335-445B-9443-6B5A6CA3C110}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.MongoDb.Tests", "test\OpenIddict.MongoDb.Tests\OpenIddict.MongoDb.Tests.csproj", "{27F603EF-D335-445B-9443-6B5A6CA3C110}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{D8075F1F-6257-463B-B481-BDC7C5ABA292}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.Extensions", "shared\OpenIddict.Extensions\OpenIddict.Extensions.csproj", "{B90761B9-7582-44CB-AB0D-3C4058693227}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -157,6 +161,10 @@ Global
{27F603EF-D335-445B-9443-6B5A6CA3C110}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27F603EF-D335-445B-9443-6B5A6CA3C110}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27F603EF-D335-445B-9443-6B5A6CA3C110}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B90761B9-7582-44CB-AB0D-3C4058693227}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B90761B9-7582-44CB-AB0D-3C4058693227}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B90761B9-7582-44CB-AB0D-3C4058693227}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B90761B9-7582-44CB-AB0D-3C4058693227}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -184,6 +192,7 @@ Global
{14C55FB6-9626-4BDE-8961-3BE91DDD6418} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{8FACE85E-EF8F-4AB1-85DD-4010D5E2165D} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{27F603EF-D335-445B-9443-6B5A6CA3C110} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
+ {B90761B9-7582-44CB-AB0D-3C4058693227} = {D8075F1F-6257-463B-B481-BDC7C5ABA292}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616}
diff --git a/src/OpenIddict.Core/OpenIddictCoreHelpers.cs b/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
similarity index 70%
rename from src/OpenIddict.Core/OpenIddictCoreHelpers.cs
rename to shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
index 65090289..8026a646 100644
--- a/src/OpenIddict.Core/OpenIddictCoreHelpers.cs
+++ b/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
@@ -1,23 +1,31 @@
using System;
-using System.ComponentModel;
+using System.Collections.Generic;
using System.Linq;
using System.Reflection;
-namespace OpenIddict.Core
+namespace OpenIddict.Extensions
{
///
/// Exposes common helpers used by the OpenIddict assemblies.
///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static class OpenIddictCoreHelpers
+ internal static class OpenIddictHelpers
{
///
- /// Finds the base type that matches the specified generic type definition.
+ /// Finds the first base type that matches the specified generic type definition.
///
/// The type to introspect.
/// The generic type definition.
/// A instance if the base type was found, null otherwise.
public static Type FindGenericBaseType(Type type, Type definition)
+ => FindGenericBaseTypes(type, definition).FirstOrDefault();
+
+ ///
+ /// Finds all the base types that matches the specified generic type definition.
+ ///
+ /// The type to introspect.
+ /// The generic type definition.
+ /// A instance if the base type was found, null otherwise.
+ public static IEnumerable FindGenericBaseTypes(Type type, Type definition)
{
if (type == null)
{
@@ -45,7 +53,7 @@ namespace OpenIddict.Core
if (contract.GetGenericTypeDefinition() == definition)
{
- return contract;
+ yield return contract;
}
}
}
@@ -61,12 +69,10 @@ namespace OpenIddict.Core
if (candidate.GetGenericTypeDefinition() == definition)
{
- return candidate;
+ yield return candidate;
}
}
}
-
- return null;
}
}
}
diff --git a/shared/OpenIddict.Extensions/OpenIddict.Extensions.csproj b/shared/OpenIddict.Extensions/OpenIddict.Extensions.csproj
new file mode 100644
index 00000000..e613eb64
--- /dev/null
+++ b/shared/OpenIddict.Extensions/OpenIddict.Extensions.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+
+ netstandard1.3
+ false
+
+
+
+
+
+
+
diff --git a/src/OpenIddict.Core/OpenIddict.Core.csproj b/src/OpenIddict.Core/OpenIddict.Core.csproj
index a657d1f3..70acc746 100644
--- a/src/OpenIddict.Core/OpenIddict.Core.csproj
+++ b/src/OpenIddict.Core/OpenIddict.Core.csproj
@@ -30,4 +30,8 @@
+
+
+
+
diff --git a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
index 9c8a4ec4..9fdff98c 100644
--- a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
+++ b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
@@ -11,6 +11,7 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.Abstractions;
using OpenIddict.Core;
+using OpenIddict.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
@@ -87,7 +88,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictApplicationStore<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictApplicationStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -144,7 +145,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictAuthorizationStore<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictAuthorizationStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -201,7 +202,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictScopeStore<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictScopeStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -258,7 +259,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictTokenStore<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(IOpenIddictTokenStore<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -315,7 +316,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictApplicationManager<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictApplicationManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -412,7 +413,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictAuthorizationManager<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictAuthorizationManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -508,7 +509,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictScopeManager<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictScopeManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
@@ -605,7 +606,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(type));
}
- var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictTokenManager<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(type, typeof(OpenIddictTokenManager<>));
if (root == null)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
diff --git a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj
index 73683e1a..460385f8 100644
--- a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj
+++ b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj
@@ -1,4 +1,4 @@
-
+
@@ -23,4 +23,8 @@
+
+
+
+
diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs
index a5937e77..dee8b6f9 100644
--- a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs
+++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TApplication), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs
index 217266cd..2ce3caee 100644
--- a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs
+++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs
index 52bfb7f3..55dd56a6 100644
--- a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs
+++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TScope), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs
index 74730a68..3110f8dc 100644
--- a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs
+++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFramework.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFramework
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFramework
var type = _cache.GetOrAdd(typeof(TToken), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj b/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj
index ad1e45bc..268458b2 100644
--- a/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj
+++ b/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj
@@ -23,4 +23,8 @@
+
+
+
+
diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs
index c6243669..f648f2fa 100644
--- a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TApplication), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs
index ea198cf0..2ae6ce18 100644
--- a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs
@@ -8,12 +8,11 @@ using System;
using System.Collections.Concurrent;
using System.Text;
using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@@ -50,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs
index fa8f548c..6c072d00 100644
--- a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TScope), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs
index 74b58473..c9edcc49 100644
--- a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs
@@ -11,8 +11,8 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
-using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
+using OpenIddict.Extensions;
namespace OpenIddict.EntityFrameworkCore
{
@@ -49,7 +49,7 @@ namespace OpenIddict.EntityFrameworkCore
var type = _cache.GetOrAdd(typeof(TToken), key =>
{
- var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
+ var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs b/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
index 0509a5be..b2746180 100644
--- a/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
+++ b/src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
@@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
-using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -20,12 +19,10 @@ namespace OpenIddict.Server
/// 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.
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result determines whether next handlers in the pipeline are invoked.
///
- Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
+ Task HandleAsync([NotNull] TEvent notification);
}
}
diff --git a/src/OpenIddict.Server/IOpenIddictServerEventService.cs b/src/OpenIddict.Server/IOpenIddictServerEventService.cs
new file mode 100644
index 00000000..81a4944d
--- /dev/null
+++ b/src/OpenIddict.Server/IOpenIddictServerEventService.cs
@@ -0,0 +1,26 @@
+/*
+ * 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.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.
+ /// A that can be used to monitor the asynchronous operation.
+ Task PublishAsync([NotNull] TEvent notification) where TEvent : class, IOpenIddictServerEvent;
+ }
+}
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerEventService.cs b/src/OpenIddict.Server/Internal/OpenIddictServerEventService.cs
deleted file mode 100644
index ce91a4b1..00000000
--- a/src/OpenIddict.Server/Internal/OpenIddictServerEventService.cs
+++ /dev/null
@@ -1,164 +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;
-using System.Threading.Tasks;
-using AspNet.Security.OpenIdConnect.Primitives;
-using JetBrains.Annotations;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.DependencyInjection;
-using static OpenIddict.Server.OpenIddictServerEvents;
-
-namespace OpenIddict.Server.Internal
-{
- ///
- /// Dispatches events by invoking the corresponding notification handlers.
- /// Note: this API supports the OpenIddict infrastructure and is not intended to be used
- /// directly from your code. This API may change or be removed in future minor releases.
- ///
- public class OpenIddictServerEventService
- {
- private readonly IServiceProvider _provider;
-
- ///
- /// Creates a new instance of the class.
- /// Note: this API supports the OpenIddict infrastructure and is not intended to be used
- /// directly from your code. This API may change or be removed in future minor releases.
- ///
- 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.State != EventResultState.Continue: 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.State != EventResultState.Continue: return;
- case ExtractConfigurationRequest value when value.Context.State != EventResultState.Continue: return;
- case ExtractCryptographyRequest value when value.Context.State != EventResultState.Continue: return;
- case ExtractIntrospectionRequest value when value.Context.State != EventResultState.Continue: return;
- case ExtractLogoutRequest value when value.Context.State != EventResultState.Continue: return;
- case ExtractRevocationRequest value when value.Context.State != EventResultState.Continue: return;
- case ExtractTokenRequest value when value.Context.State != EventResultState.Continue: return;
- case ExtractUserinfoRequest value when value.Context.State != EventResultState.Continue: return;
-
- case ValidateAuthorizationRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateConfigurationRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateCryptographyRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateIntrospectionRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateLogoutRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateRevocationRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateTokenRequest value when value.Context.State != EventResultState.Continue: return;
- case ValidateUserinfoRequest value when value.Context.State != EventResultState.Continue: 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.State != EventResultState.Continue: return;
- case HandleConfigurationRequest value when value.Context.State != EventResultState.Continue: return;
- case HandleCryptographyRequest value when value.Context.State != EventResultState.Continue: return;
- case HandleIntrospectionRequest value when value.Context.State != EventResultState.Continue: return;
- case HandleLogoutRequest value when value.Context.State != EventResultState.Continue: return;
- case HandleRevocationRequest value when value.Context.State != EventResultState.Continue: return;
- case HandleTokenRequest value when value.Context.State != EventResultState.Continue: return;
- case HandleUserinfoRequest value when value.Context.State != EventResultState.Continue: 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.State != EventResultState.Continue: return;
- case ProcessSigninResponse value when value.Context.State != EventResultState.Continue: return;
- case ProcessSignoutResponse value when value.Context.State != EventResultState.Continue: 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.State != EventResultState.Continue: return;
- case ApplyConfigurationResponse value when value.Context.State != EventResultState.Continue: return;
- case ApplyCryptographyResponse value when value.Context.State != EventResultState.Continue: return;
- case ApplyIntrospectionResponse value when value.Context.State != EventResultState.Continue: return;
- case ApplyLogoutResponse value when value.Context.State != EventResultState.Continue: return;
- case ApplyRevocationResponse value when value.Context.State != EventResultState.Continue: return;
- case ApplyTokenResponse value when value.Context.State != EventResultState.Continue: return;
- case ApplyUserinfoResponse value when value.Context.State != EventResultState.Continue: return;
-
- case DeserializeAuthorizationCode value when value.Context.State != EventResultState.Continue: return;
- case DeserializeAccessToken value when value.Context.State != EventResultState.Continue: return;
- case DeserializeIdentityToken value when value.Context.State != EventResultState.Continue: return;
- case DeserializeRefreshToken value when value.Context.State != EventResultState.Continue: 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.State != EventResultState.Continue: return;
- case SerializeAccessToken value when value.Context.State != EventResultState.Continue: return;
- case SerializeIdentityToken value when value.Context.State != EventResultState.Continue: return;
- case SerializeRefreshToken value when value.Context.State != EventResultState.Continue: 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/Internal/OpenIddictServerProvider.Helpers.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
index d038223d..2490132e 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
@@ -684,8 +684,8 @@ namespace OpenIddict.Server.Internal
private static ILogger GetLogger(IServiceProvider provider)
=> provider.GetRequiredService>();
- private static OpenIddictServerEventService GetEventService(IServiceProvider provider)
- => provider.GetRequiredService();
+ private static IOpenIddictServerEventService GetEventService(IServiceProvider provider)
+ => provider.GetRequiredService();
private static IOpenIddictApplicationManager GetApplicationManager(IServiceProvider provider)
=> provider.GetService() ?? throw new InvalidOperationException(new StringBuilder()
diff --git a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
index d29ef561..6fd34027 100644
--- a/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
+++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
@@ -5,7 +5,6 @@
*/
using System;
-using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
diff --git a/src/OpenIddict.Server/OpenIddict.Server.csproj b/src/OpenIddict.Server/OpenIddict.Server.csproj
index b190854c..5c19fc3d 100644
--- a/src/OpenIddict.Server/OpenIddict.Server.csproj
+++ b/src/OpenIddict.Server/OpenIddict.Server.csproj
@@ -24,4 +24,8 @@
+
+
+
+
diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs
index 9754a454..855d8ab6 100644
--- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs
+++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs
@@ -12,7 +12,6 @@ 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 JetBrains.Annotations;
@@ -21,6 +20,7 @@ using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.IdentityModel.Tokens;
+using OpenIddict.Extensions;
using OpenIddict.Server;
namespace Microsoft.Extensions.DependencyInjection
@@ -51,13 +51,13 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
///
- /// Registers an event handler for the specified event type.
+ /// Registers an inline event handler for the specified event type.
///
- /// The handler added to the DI container.
+ /// The handler delegate.
/// The .
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler(
- [NotNull] IOpenIddictServerEventHandler handler)
+ [NotNull] Func> handler)
where TEvent : class, IOpenIddictServerEvent
{
if (handler == null)
@@ -65,62 +65,30 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(handler));
}
- Services.AddSingleton(handler);
+ Services.AddSingleton>(
+ new OpenIddictServerEventHandler(handler));
return this;
}
///
- /// Registers an event handler for the specified event type.
+ /// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
///
- /// 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));
+ public OpenIddictServerBuilder AddEventHandler(ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ => AddEventHandler(typeof(THandler), lifetime);
///
- /// Registers an event handler for the specified event type.
+ /// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
///
/// 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
+ public OpenIddictServerBuilder AddEventHandler([NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
if (type == null)
{
@@ -132,12 +100,21 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
- if (!typeof(IOpenIddictServerEventHandler).IsAssignableFrom(type))
+ if (type.GetTypeInfo().IsGenericTypeDefinition)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
- Services.Add(new ServiceDescriptor(typeof(IOpenIddictServerEventHandler), type, lifetime));
+ var services = OpenIddictHelpers.FindGenericBaseTypes(type, typeof(IOpenIddictServerEventHandler<>)).ToArray();
+ if (services.Length == 0)
+ {
+ throw new ArgumentException("The specified type is invalid.", nameof(type));
+ }
+
+ foreach (var service in services)
+ {
+ Services.Add(new ServiceDescriptor(service, type, lifetime));
+ }
return this;
}
diff --git a/src/OpenIddict.Server/OpenIddictServerEventHandler.cs b/src/OpenIddict.Server/OpenIddictServerEventHandler.cs
index ef6aaf68..b0d9e490 100644
--- a/src/OpenIddict.Server/OpenIddictServerEventHandler.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEventHandler.cs
@@ -5,7 +5,6 @@
*/
using System;
-using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -18,35 +17,24 @@ namespace OpenIddict.Server
public class OpenIddictServerEventHandler : IOpenIddictServerEventHandler
where TEvent : class, IOpenIddictServerEvent
{
- private readonly Func _handler;
+ private readonly Func> _handler;
///
/// Creates a new event using the specified handler delegate.
///
- /// The event handler delegate
- public OpenIddictServerEventHandler([NotNull] Func handler)
+ /// 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.
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result determines whether next handlers in the pipeline are invoked.
///
- public async Task HandleAsync(TEvent notification, CancellationToken cancellationToken)
- {
- if (notification == null)
- {
- throw new ArgumentNullException(nameof(notification));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await _handler.Invoke(notification, cancellationToken);
- }
+ public Task HandleAsync(TEvent notification)
+ => _handler(notification ?? throw new ArgumentNullException(nameof(notification)));
}
}
diff --git a/src/OpenIddict.Server/OpenIddictServerEventService.cs b/src/OpenIddict.Server/OpenIddictServerEventService.cs
new file mode 100644
index 00000000..f53ec8cf
--- /dev/null
+++ b/src/OpenIddict.Server/OpenIddictServerEventService.cs
@@ -0,0 +1,52 @@
+/*
+ * 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 JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Dispatches events by invoking the corresponding notification handlers.
+ ///
+ public class OpenIddictServerEventService : IOpenIddictServerEventService
+ {
+ private readonly IServiceProvider _provider;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public OpenIddictServerEventService([NotNull] IServiceProvider provider)
+ => _provider = provider;
+
+ ///
+ /// Publishes a new event.
+ ///
+ /// The type of the event to publish.
+ /// The event to publish.
+ /// A that can be used to monitor the asynchronous operation.
+ public async Task PublishAsync([NotNull] TEvent notification) where TEvent : class, IOpenIddictServerEvent
+ {
+ if (notification == null)
+ {
+ throw new ArgumentNullException(nameof(notification));
+ }
+
+ foreach (var handler in _provider.GetServices>())
+ {
+ switch (await handler.HandleAsync(notification))
+ {
+ case OpenIddictServerEventState.Unhandled: continue;
+ case OpenIddictServerEventState.Handled: return;
+
+ default: throw new InvalidOperationException("The specified event state is not valid.");
+ }
+ }
+ }
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerEventState.cs b/src/OpenIddict.Server/OpenIddictServerEventState.cs
new file mode 100644
index 00000000..37145e2c
--- /dev/null
+++ b/src/OpenIddict.Server/OpenIddictServerEventState.cs
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+namespace OpenIddict.Server
+{
+ ///
+ /// Represents the state of an event triggered by the OpenIddict
+ /// server components and processed by user-defined handlers.
+ ///
+ public enum OpenIddictServerEventState
+ {
+ ///
+ /// Marks the event as unhandled, allowing the event service to invoke the
+ /// other event handlers registered in the dependency injection container.
+ /// Using this value is recommended for event handlers that don't produce
+ /// an immediate response (i.e that don't call context.HandleResponse(),
+ /// context.SkipToNextMiddleware(), context.Validate() or context.Reject()).
+ ///
+ Unhandled = 0,
+
+ ///
+ /// Marks the event as fully handled, preventing the event service from invoking
+ /// other event handlers registered in the dependency injection container.
+ /// Using this value is recommended for event handlers that produce an
+ /// immediate response (i.e that call context.SkipToNextMiddleware(),
+ /// context.HandleResponse(), context.Validate() or context.Reject()).
+ ///
+ Handled = 1
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
index 64c319ce..017579f6 100644
--- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
@@ -46,7 +46,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddMemoryCache();
builder.Services.AddOptions();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddScoped();
return new OpenIddictServerBuilder(builder.Services);
}
diff --git a/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs b/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
index 9e60cc1d..2840ee43 100644
--- a/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
+++ b/src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
@@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
-using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -20,12 +19,10 @@ namespace OpenIddict.Validation
/// 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.
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result determines whether next handlers in the pipeline are invoked.
///
- Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
+ Task HandleAsync([NotNull] TEvent notification);
}
}
diff --git a/src/OpenIddict.Validation/IOpenIddictValidationEventService.cs b/src/OpenIddict.Validation/IOpenIddictValidationEventService.cs
new file mode 100644
index 00000000..05b7bcf0
--- /dev/null
+++ b/src/OpenIddict.Validation/IOpenIddictValidationEventService.cs
@@ -0,0 +1,25 @@
+/*
+ * 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.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.
+ /// A that can be used to monitor the asynchronous operation.
+ Task PublishAsync([NotNull] TEvent notification) where TEvent : class, IOpenIddictValidationEvent;
+ }
+}
diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationEventService.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationEventService.cs
deleted file mode 100644
index ca51267b..00000000
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationEventService.cs
+++ /dev/null
@@ -1,77 +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;
-using System.Threading.Tasks;
-using JetBrains.Annotations;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace OpenIddict.Validation.Internal
-{
- ///
- /// Dispatches events by invoking the corresponding notification handlers.
- /// Note: this API supports the OpenIddict infrastructure and is not intended to be used
- /// directly from your code. This API may change or be removed in future minor releases.
- ///
- public class OpenIddictValidationEventService
- {
- private readonly IServiceProvider _provider;
-
- ///
- /// Creates a new instance of the class.
- /// Note: this API supports the OpenIddict infrastructure and is not intended to be used
- /// directly from your code. This API may change or be removed in future minor releases.
- ///
- 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.State != EventResultState.Continue: return;
-
- case OpenIddictValidationEvents.CreateTicket value when value.Context.State != EventResultState.Continue: return;
- case OpenIddictValidationEvents.CreateTicket value when value.Context.Ticket == null: return;
-
- case OpenIddictValidationEvents.DecryptToken value when value.Context.State != EventResultState.Continue: return;
- case OpenIddictValidationEvents.DecryptToken value when value.Context.Ticket != null: return;
-
- case OpenIddictValidationEvents.RetrieveToken value when value.Context.State != EventResultState.Continue: return;
- case OpenIddictValidationEvents.RetrieveToken value when value.Context.Ticket != null: return;
-
- case OpenIddictValidationEvents.ValidateToken value when value.Context.State != EventResultState.Continue: return;
- case OpenIddictValidationEvents.ValidateToken value when value.Context.Ticket == null: return;
- }
- }
- }
- }
-}
diff --git a/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
index ee9167e2..ec83d6d1 100644
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
+++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
@@ -23,11 +23,11 @@ namespace OpenIddict.Validation.Internal
public sealed class OpenIddictValidationProvider : OAuthValidationEvents
{
public override Task ApplyChallenge([NotNull] ApplyChallengeContext context)
- => context.HttpContext.RequestServices.GetRequiredService()
+ => GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.ApplyChallenge(context));
public override Task CreateTicket([NotNull] CreateTicketContext context)
- => context.HttpContext.RequestServices.GetRequiredService()
+ => GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.CreateTicket(context));
public override async Task DecryptToken([NotNull] DecryptTokenContext context)
@@ -47,7 +47,7 @@ namespace OpenIddict.Validation.Internal
.ToString());
}
- var logger = context.HttpContext.RequestServices.GetRequiredService>();
+ var logger = GetLogger(context.HttpContext.RequestServices);
// Retrieve the token entry from the database. If it
// cannot be found, assume the token is not valid.
@@ -103,12 +103,12 @@ namespace OpenIddict.Validation.Internal
context.HandleResponse();
}
- await context.HttpContext.RequestServices.GetRequiredService()
+ await GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.DecryptToken(context));
}
public override Task RetrieveToken([NotNull] RetrieveTokenContext context)
- => context.HttpContext.RequestServices.GetRequiredService()
+ => GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.RetrieveToken(context));
public override async Task ValidateToken([NotNull] ValidateTokenContext context)
@@ -128,7 +128,7 @@ namespace OpenIddict.Validation.Internal
.ToString());
}
- var logger = context.HttpContext.RequestServices.GetRequiredService>();
+ var logger = GetLogger(context.HttpContext.RequestServices);
var identifier = context.Ticket.Properties.GetProperty(OpenIddictConstants.Properties.InternalAuthorizationId);
if (!string.IsNullOrEmpty(identifier))
@@ -145,8 +145,14 @@ namespace OpenIddict.Validation.Internal
}
}
- await context.HttpContext.RequestServices.GetRequiredService()
+ await GetEventService(context.HttpContext.RequestServices)
.PublishAsync(new OpenIddictValidationEvents.ValidateToken(context));
}
+
+ private static ILogger GetLogger(IServiceProvider provider)
+ => provider.GetRequiredService>();
+
+ private static IOpenIddictValidationEventService GetEventService(IServiceProvider provider)
+ => provider.GetRequiredService();
}
}
diff --git a/src/OpenIddict.Validation/OpenIddict.Validation.csproj b/src/OpenIddict.Validation/OpenIddict.Validation.csproj
index 0847e0f1..d888938d 100644
--- a/src/OpenIddict.Validation/OpenIddict.Validation.csproj
+++ b/src/OpenIddict.Validation/OpenIddict.Validation.csproj
@@ -22,4 +22,8 @@
+
+
+
+
diff --git a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
index d7fb9d18..c61ff7ef 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
@@ -8,10 +8,10 @@ using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
-using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
+using OpenIddict.Extensions;
using OpenIddict.Validation;
namespace Microsoft.Extensions.DependencyInjection
@@ -42,13 +42,13 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
///
- /// Registers an event handler for the specified event type.
+ /// Registers an inline event handler for the specified event type.
///
- /// The handler added to the DI container.
+ /// The handler delegate.
/// The .
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler(
- [NotNull] IOpenIddictValidationEventHandler handler)
+ [NotNull] Func> handler)
where TEvent : class, IOpenIddictValidationEvent
{
if (handler == null)
@@ -56,62 +56,30 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(handler));
}
- Services.AddSingleton(handler);
+ Services.AddSingleton>(
+ new OpenIddictValidationEventHandler(handler));
return this;
}
///
- /// Registers an event handler for the specified event type.
+ /// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
///
- /// The handler added to the DI container.
- /// The .
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictValidationBuilder AddEventHandler([NotNull] Func handler)
- where TEvent : class, IOpenIddictValidationEvent
- => 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 OpenIddictValidationBuilder AddEventHandler([NotNull] Func handler)
- where TEvent : class, IOpenIddictValidationEvent
- {
- if (handler == null)
- {
- throw new ArgumentNullException(nameof(handler));
- }
-
- return AddEventHandler(new OpenIddictValidationEventHandler(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 OpenIddictValidationBuilder AddEventHandler(
- ServiceLifetime lifetime = ServiceLifetime.Scoped)
- where TEvent : class, IOpenIddictValidationEvent
- where THandler : IOpenIddictValidationEventHandler
- => AddEventHandler(typeof(THandler));
+ public OpenIddictValidationBuilder AddEventHandler(ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ => AddEventHandler(typeof(THandler), lifetime);
///
- /// Registers an event handler for the specified event type.
+ /// Registers an event handler that will be invoked for all the events listed by the implemented interfaces.
///
/// The type of the handler.
/// The lifetime of the registered service.
/// The .
[EditorBrowsable(EditorBrowsableState.Advanced)]
- public OpenIddictValidationBuilder AddEventHandler(
- [NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
- where TEvent : class, IOpenIddictValidationEvent
+ public OpenIddictValidationBuilder AddEventHandler([NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
if (type == null)
{
@@ -123,12 +91,21 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
- if (!typeof(IOpenIddictValidationEventHandler).IsAssignableFrom(type))
+ if (type.GetTypeInfo().IsGenericTypeDefinition)
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
- Services.Add(new ServiceDescriptor(typeof(IOpenIddictValidationEventHandler), type, lifetime));
+ var services = OpenIddictHelpers.FindGenericBaseTypes(type, typeof(IOpenIddictValidationEventHandler<>)).ToArray();
+ if (services.Length == 0)
+ {
+ throw new ArgumentException("The specified type is invalid.", nameof(type));
+ }
+
+ foreach (var service in services)
+ {
+ Services.Add(new ServiceDescriptor(service, type, lifetime));
+ }
return this;
}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs b/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
index e43b265d..b3152c75 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
@@ -5,7 +5,6 @@
*/
using System;
-using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -18,35 +17,24 @@ namespace OpenIddict.Validation
public class OpenIddictValidationEventHandler : IOpenIddictValidationEventHandler
where TEvent : class, IOpenIddictValidationEvent
{
- private readonly Func _handler;
+ private readonly Func> _handler;
///
/// Creates a new event using the specified handler delegate.
///
- /// The event handler delegate
- public OpenIddictValidationEventHandler([NotNull] Func handler)
+ /// 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.
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result determines whether next handlers in the pipeline are invoked.
///
- public async Task HandleAsync(TEvent notification, CancellationToken cancellationToken)
- {
- if (notification == null)
- {
- throw new ArgumentNullException(nameof(notification));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await _handler.Invoke(notification, cancellationToken);
- }
+ public Task HandleAsync(TEvent notification)
+ => _handler(notification ?? throw new ArgumentNullException(nameof(notification)));
}
}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEventService.cs b/src/OpenIddict.Validation/OpenIddictValidationEventService.cs
new file mode 100644
index 00000000..e2f09056
--- /dev/null
+++ b/src/OpenIddict.Validation/OpenIddictValidationEventService.cs
@@ -0,0 +1,52 @@
+/*
+ * 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 JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Dispatches events by invoking the corresponding notification handlers.
+ ///
+ public class OpenIddictValidationEventService : IOpenIddictValidationEventService
+ {
+ private readonly IServiceProvider _provider;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public OpenIddictValidationEventService([NotNull] IServiceProvider provider)
+ => _provider = provider;
+
+ ///
+ /// Publishes a new event.
+ ///
+ /// The type of the event to publish.
+ /// The event to publish.
+ /// A that can be used to monitor the asynchronous operation.
+ public async Task PublishAsync([NotNull] TEvent notification) where TEvent : class, IOpenIddictValidationEvent
+ {
+ if (notification == null)
+ {
+ throw new ArgumentNullException(nameof(notification));
+ }
+
+ foreach (var handler in _provider.GetServices>())
+ {
+ switch (await handler.HandleAsync(notification))
+ {
+ case OpenIddictValidationEventState.Unhandled: continue;
+ case OpenIddictValidationEventState.Handled: return;
+
+ default: throw new InvalidOperationException("The specified event state is not valid.");
+ }
+ }
+ }
+ }
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationEventState.cs b/src/OpenIddict.Validation/OpenIddictValidationEventState.cs
new file mode 100644
index 00000000..b3b47569
--- /dev/null
+++ b/src/OpenIddict.Validation/OpenIddictValidationEventState.cs
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+namespace OpenIddict.Validation
+{
+ ///
+ /// Represents the state of an event triggered by the OpenIddict
+ /// validation components and processed by user-defined handlers.
+ ///
+ public enum OpenIddictValidationEventState
+ {
+ ///
+ /// Marks the event as unhandled, allowing the event service to invoke the
+ /// other event handlers registered in the dependency injection container.
+ /// Using this value is recommended for event handlers that don't produce
+ /// an immediate response (i.e that don't call context.HandleResponse(),
+ /// context.Fail(), context.NoResult() or context.Success()).
+ ///
+ Unhandled = 0,
+
+ ///
+ /// Marks the event as fully handled, preventing the event service from invoking
+ /// other event handlers registered in the dependency injection container.
+ /// Using this value is recommended for event handlers that produce an
+ /// immediate response (i.e that call context.HandleResponse(),
+ /// context.Fail(), context.NoResult() or context.Success()).
+ ///
+ Handled = 1
+ }
+}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
index 6b8799e2..717905f1 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
@@ -42,7 +42,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddLogging();
builder.Services.AddOptions();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddScoped();
return new OpenIddictValidationBuilder(builder.Services);
}
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
index 91740620..a7c00772 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
@@ -7,7 +7,6 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
-using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.DataProtection;
@@ -42,20 +41,18 @@ namespace OpenIddict.Server.Tests
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
- var handler = new OpenIddictServerEventHandler(
- (notification, cancellationToken) => Task.FromResult(0));
// Act
- builder.AddEventHandler(handler);
+ builder.AddEventHandler(notification => Task.FromResult(OpenIddictServerEventState.Handled));
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler) &&
- service.ImplementationInstance == handler);
+ service.ImplementationInstance.GetType() == typeof(OpenIddictServerEventHandler));
}
[Fact]
- public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
+ public void AddEventHandler_ThrowsAnExceptionForUnsupportedLifetime()
{
// Arrange
var services = CreateServices();
@@ -64,7 +61,41 @@ namespace OpenIddict.Server.Tests
// Act and assert
var exception = Assert.Throws(delegate
{
- return builder.AddEventHandler(typeof(object));
+ return builder.AddEventHandler(ServiceLifetime.Transient);
+ });
+
+ Assert.Equal("lifetime", exception.ParamName);
+ Assert.StartsWith("Handlers cannot be registered as transient services.", exception.Message);
+ }
+
+ [Fact]
+ public void AddEventHandler_ThrowsAnExceptionForOpenGenericHandlerType()
+ {
+ // Arrange
+ var services = CreateServices();
+ var builder = CreateBuilder(services);
+
+ // Act and assert
+ var exception = Assert.Throws(delegate
+ {
+ return builder.AddEventHandler(typeof(OpenIddictServerEventHandler<>));
+ });
+
+ Assert.Equal("type", exception.ParamName);
+ Assert.StartsWith("The specified type is invalid.", exception.Message);
+ }
+
+ [Fact]
+ public void AddEventHandler_ThrowsAnExceptionForNonHandlerType()
+ {
+ // 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);
@@ -79,12 +110,17 @@ namespace OpenIddict.Server.Tests
var builder = CreateBuilder(services);
// Act
- builder.AddEventHandler();
+ builder.AddEventHandler(ServiceLifetime.Singleton);
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler) &&
- service.ImplementationType == typeof(CustomHandler));
+ service.ImplementationType == typeof(CustomHandler) &&
+ service.Lifetime == ServiceLifetime.Singleton);
+ Assert.Contains(services, service =>
+ service.ServiceType == typeof(IOpenIddictServerEventHandler) &&
+ service.ImplementationType == typeof(CustomHandler) &&
+ service.Lifetime == ServiceLifetime.Singleton);
}
[Fact]
@@ -802,10 +838,17 @@ namespace OpenIddict.Server.Tests
return options.Value;
}
- public class CustomHandler : OpenIddictServerEventHandler
+ public class CustomHandler : IOpenIddictServerEventHandler,
+ IOpenIddictServerEventHandler
{
- public CustomHandler(Func handler) : base(handler)
+ public Task HandleAsync(ApplyAuthorizationResponse notification)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task HandleAsync(HandleAuthorizationRequest notification)
{
+ throw new NotImplementedException();
}
}
}
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerEventHandlerTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerEventHandlerTests.cs
new file mode 100644
index 00000000..eb24c800
--- /dev/null
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerEventHandlerTests.cs
@@ -0,0 +1,60 @@
+/*
+ * 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 Xunit;
+
+namespace OpenIddict.Server.Tests
+{
+ public class OpenIddictServerEventHandlerTests
+ {
+ [Fact]
+ public void Constructor_ThrowsAnExceptionForNullHandler()
+ {
+ // Arrange, act and assert
+ var exception = Assert.Throws(()
+ => new OpenIddictServerEventHandler(handler: null));
+
+ Assert.Equal("handler", exception.ParamName);
+ }
+
+ [Fact]
+ public async Task HandleAsync_ThrowsAnExceptionForNullNotification()
+ {
+ // Arrange
+ var handler = new OpenIddictServerEventHandler(
+ notification => Task.FromResult(OpenIddictServerEventState.Handled));
+
+ // Act and assert
+ var exception = await Assert.ThrowsAsync(()
+ => handler.HandleAsync(notification: null));
+
+ Assert.Equal("notification", exception.ParamName);
+ }
+
+ [Fact]
+ public async Task HandleAsync_InvokesInlineHandler()
+ {
+ // Arrange
+ var marker = false;
+ var handler = new OpenIddictServerEventHandler(
+ notification =>
+ {
+ marker = true;
+ return Task.FromResult(OpenIddictServerEventState.Handled);
+ });
+
+ // Act
+ await handler.HandleAsync(new Event());
+
+ // Assert
+ Assert.True(marker);
+ }
+
+ public class Event : IOpenIddictServerEvent { }
+ }
+}
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerEventServiceTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerEventServiceTests.cs
new file mode 100644
index 00000000..6d3ecbc0
--- /dev/null
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerEventServiceTests.cs
@@ -0,0 +1,92 @@
+/*
+ * 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.Collections.Generic;
+using System.Threading.Tasks;
+using Moq;
+using Xunit;
+
+namespace OpenIddict.Server.Tests
+{
+ public class OpenIddictServerEventServiceTests
+ {
+ [Fact]
+ public async Task PublishAsync_ThrowsAnExceptionForNullNotification()
+ {
+ // Arrange
+ var provider = Mock.Of();
+ var service = new OpenIddictServerEventService(provider);
+
+ // Act and assert
+ var exception = await Assert.ThrowsAsync(()
+ => service.PublishAsync(notification: null));
+
+ Assert.Equal("notification", exception.ParamName);
+ }
+
+ [Fact]
+ public async Task PublishAsync_InvokesHandlers()
+ {
+ // Arrange
+ var handlers = new List>
+ {
+ Mock.Of>(),
+ Mock.Of>()
+ };
+
+ var provider = new Mock();
+ provider.Setup(mock => mock.GetService(typeof(IEnumerable>)))
+ .Returns(handlers);
+
+ var service = new OpenIddictServerEventService(provider.Object);
+
+ var notification = new Event();
+
+ // Act
+ await service.PublishAsync(notification);
+
+ // Assert
+ Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ }
+
+ [Fact]
+ public async Task PublishAsync_StopsInvokingHandlersWhenHandledIsReturned()
+ {
+ // Arrange
+ var handlers = new List>
+ {
+ Mock.Of>(
+ mock => mock.HandleAsync(It.IsAny()) == Task.FromResult(OpenIddictServerEventState.Unhandled)),
+ Mock.Of>(
+ mock => mock.HandleAsync(It.IsAny()) == Task.FromResult(OpenIddictServerEventState.Unhandled)),
+ Mock.Of>(
+ mock => mock.HandleAsync(It.IsAny()) == Task.FromResult(OpenIddictServerEventState.Handled)),
+ Mock.Of>()
+ };
+
+ var provider = new Mock();
+ provider.Setup(mock => mock.GetService(typeof(IEnumerable>)))
+ .Returns(handlers);
+
+ var service = new OpenIddictServerEventService(provider.Object);
+
+ var notification = new Event();
+
+ // Act
+ await service.PublishAsync(notification);
+
+ // Assert
+ Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[2]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[3]).Verify(mock => mock.HandleAsync(notification), Times.Never());
+ }
+
+ public class Event : IOpenIddictServerEvent { }
+ }
+}
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
index 8e1183ee..79c492a4 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
@@ -105,7 +105,7 @@ namespace OpenIddict.Server.Tests
// Assert
Assert.Contains(services, service => service.Lifetime == ServiceLifetime.Scoped &&
- service.ServiceType == typeof(OpenIddictServerEventService) &&
+ service.ServiceType == typeof(IOpenIddictServerEventService) &&
service.ImplementationType == typeof(OpenIddictServerEventService));
}
diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
index 1ced0ed2..cb75311a 100644
--- a/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
@@ -5,7 +5,6 @@
*/
using System;
-using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
@@ -35,20 +34,18 @@ namespace OpenIddict.Validation.Tests
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
- var handler = new OpenIddictValidationEventHandler(
- (notification, cancellationToken) => Task.FromResult(0));
// Act
- builder.AddEventHandler(handler);
+ builder.AddEventHandler(notification => Task.FromResult(OpenIddictValidationEventState.Handled));
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler) &&
- service.ImplementationInstance == handler);
+ service.ImplementationInstance.GetType() == typeof(OpenIddictValidationEventHandler));
}
[Fact]
- public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
+ public void AddEventHandler_ThrowsAnExceptionForUnsupportedLifetime()
{
// Arrange
var services = CreateServices();
@@ -57,7 +54,41 @@ namespace OpenIddict.Validation.Tests
// Act and assert
var exception = Assert.Throws(delegate
{
- return builder.AddEventHandler(typeof(object));
+ return builder.AddEventHandler(ServiceLifetime.Transient);
+ });
+
+ Assert.Equal("lifetime", exception.ParamName);
+ Assert.StartsWith("Handlers cannot be registered as transient services.", exception.Message);
+ }
+
+ [Fact]
+ public void AddEventHandler_ThrowsAnExceptionForOpenGenericHandlerType()
+ {
+ // Arrange
+ var services = CreateServices();
+ var builder = CreateBuilder(services);
+
+ // Act and assert
+ var exception = Assert.Throws(delegate
+ {
+ return builder.AddEventHandler(typeof(OpenIddictValidationEventHandler<>));
+ });
+
+ Assert.Equal("type", exception.ParamName);
+ Assert.StartsWith("The specified type is invalid.", exception.Message);
+ }
+
+ [Fact]
+ public void AddEventHandler_ThrowsAnExceptionForNonHandlerType()
+ {
+ // 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);
@@ -72,12 +103,17 @@ namespace OpenIddict.Validation.Tests
var builder = CreateBuilder(services);
// Act
- builder.AddEventHandler();
+ builder.AddEventHandler(ServiceLifetime.Singleton);
// Assert
+ Assert.Contains(services, service =>
+ service.ServiceType == typeof(IOpenIddictValidationEventHandler) &&
+ service.ImplementationType == typeof(CustomHandler) &&
+ service.Lifetime == ServiceLifetime.Singleton);
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler) &&
- service.ImplementationType == typeof(CustomHandler));
+ service.ImplementationType == typeof(CustomHandler) &&
+ service.Lifetime == ServiceLifetime.Singleton);
}
[Fact]
@@ -204,10 +240,17 @@ namespace OpenIddict.Validation.Tests
return provider.GetRequiredService>().Value;
}
- public class CustomHandler : OpenIddictValidationEventHandler
+ public class CustomHandler : IOpenIddictValidationEventHandler,
+ IOpenIddictValidationEventHandler
{
- public CustomHandler(Func handler) : base(handler)
+ public Task HandleAsync(ApplyChallenge notification)
+ {
+ throw new NotImplementedException();
+ }
+
+ public Task HandleAsync(CreateTicket notification)
{
+ throw new NotImplementedException();
}
}
}
diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationEventHandlerTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationEventHandlerTests.cs
new file mode 100644
index 00000000..279352dc
--- /dev/null
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationEventHandlerTests.cs
@@ -0,0 +1,60 @@
+/*
+ * 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 Xunit;
+
+namespace OpenIddict.Validation.Tests
+{
+ public class OpenIddictValidationEventHandlerTests
+ {
+ [Fact]
+ public void Constructor_ThrowsAnExceptionForNullHandler()
+ {
+ // Arrange, act and assert
+ var exception = Assert.Throws(()
+ => new OpenIddictValidationEventHandler(handler: null));
+
+ Assert.Equal("handler", exception.ParamName);
+ }
+
+ [Fact]
+ public async Task HandleAsync_ThrowsAnExceptionForNullNotification()
+ {
+ // Arrange
+ var handler = new OpenIddictValidationEventHandler(
+ notification => Task.FromResult(OpenIddictValidationEventState.Handled));
+
+ // Act and assert
+ var exception = await Assert.ThrowsAsync(()
+ => handler.HandleAsync(notification: null));
+
+ Assert.Equal("notification", exception.ParamName);
+ }
+
+ [Fact]
+ public async Task HandleAsync_InvokesInlineHandler()
+ {
+ // Arrange
+ var marker = false;
+ var handler = new OpenIddictValidationEventHandler(
+ notification =>
+ {
+ marker = true;
+ return Task.FromResult(OpenIddictValidationEventState.Handled);
+ });
+
+ // Act
+ await handler.HandleAsync(new Event());
+
+ // Assert
+ Assert.True(marker);
+ }
+
+ public class Event : IOpenIddictValidationEvent { }
+ }
+}
diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationEventServiceTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationEventServiceTests.cs
new file mode 100644
index 00000000..b066f523
--- /dev/null
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationEventServiceTests.cs
@@ -0,0 +1,92 @@
+/*
+ * 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.Collections.Generic;
+using System.Threading.Tasks;
+using Moq;
+using Xunit;
+
+namespace OpenIddict.Validation.Tests
+{
+ public class OpenIddictValidationEventServiceTests
+ {
+ [Fact]
+ public async Task PublishAsync_ThrowsAnExceptionForNullNotification()
+ {
+ // Arrange
+ var provider = Mock.Of();
+ var service = new OpenIddictValidationEventService(provider);
+
+ // Act and assert
+ var exception = await Assert.ThrowsAsync(()
+ => service.PublishAsync(notification: null));
+
+ Assert.Equal("notification", exception.ParamName);
+ }
+
+ [Fact]
+ public async Task PublishAsync_InvokesHandlers()
+ {
+ // Arrange
+ var handlers = new List>
+ {
+ Mock.Of>(),
+ Mock.Of>()
+ };
+
+ var provider = new Mock();
+ provider.Setup(mock => mock.GetService(typeof(IEnumerable>)))
+ .Returns(handlers);
+
+ var service = new OpenIddictValidationEventService(provider.Object);
+
+ var notification = new Event();
+
+ // Act
+ await service.PublishAsync(notification);
+
+ // Assert
+ Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ }
+
+ [Fact]
+ public async Task PublishAsync_StopsInvokingHandlersWhenHandledIsReturned()
+ {
+ // Arrange
+ var handlers = new List>
+ {
+ Mock.Of>(
+ mock => mock.HandleAsync(It.IsAny()) == Task.FromResult(OpenIddictValidationEventState.Unhandled)),
+ Mock.Of>(
+ mock => mock.HandleAsync(It.IsAny()) == Task.FromResult(OpenIddictValidationEventState.Unhandled)),
+ Mock.Of>(
+ mock => mock.HandleAsync(It.IsAny()) == Task.FromResult(OpenIddictValidationEventState.Handled)),
+ Mock.Of>()
+ };
+
+ var provider = new Mock();
+ provider.Setup(mock => mock.GetService(typeof(IEnumerable>)))
+ .Returns(handlers);
+
+ var service = new OpenIddictValidationEventService(provider.Object);
+
+ var notification = new Event();
+
+ // Act
+ await service.PublishAsync(notification);
+
+ // Assert
+ Mock.Get(handlers[0]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[1]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[2]).Verify(mock => mock.HandleAsync(notification), Times.Once());
+ Mock.Get(handlers[3]).Verify(mock => mock.HandleAsync(notification), Times.Never());
+ }
+
+ public class Event : IOpenIddictValidationEvent { }
+ }
+}
diff --git a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
index 8c6624f0..4291a248 100644
--- a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
@@ -83,7 +83,7 @@ namespace OpenIddict.Validation.Tests
// Assert
Assert.Contains(services, service => service.Lifetime == ServiceLifetime.Scoped &&
- service.ServiceType == typeof(OpenIddictValidationEventService) &&
+ service.ServiceType == typeof(IOpenIddictValidationEventService) &&
service.ImplementationType == typeof(OpenIddictValidationEventService));
}