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 68%
rename from src/OpenIddict.Core/OpenIddictCoreHelpers.cs
rename to shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
index ee5f43aa..4383668d 100644
--- a/src/OpenIddict.Core/OpenIddictCoreHelpers.cs
+++ b/shared/OpenIddict.Extensions/Helpers/OpenIddictHelpers.cs
@@ -1,21 +1,30 @@
using System;
-using System.ComponentModel;
+using System.Collections.Generic;
+using System.Linq;
-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)
{
@@ -43,7 +52,7 @@ namespace OpenIddict.Core
if (contract.GetGenericTypeDefinition() == definition)
{
- return contract;
+ yield return contract;
}
}
}
@@ -59,12 +68,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..0f2e59bf
--- /dev/null
+++ b/shared/OpenIddict.Extensions/OpenIddict.Extensions.csproj
@@ -0,0 +1,10 @@
+
+
+
+
+
+ netstandard2.0
+ false
+
+
+
diff --git a/src/OpenIddict.Core/OpenIddict.Core.csproj b/src/OpenIddict.Core/OpenIddict.Core.csproj
index cc6d4cc3..45704cf9 100644
--- a/src/OpenIddict.Core/OpenIddict.Core.csproj
+++ b/src/OpenIddict.Core/OpenIddict.Core.csproj
@@ -23,4 +23,8 @@
+
+
+
+
diff --git a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
index 25112337..44aeca6d 100644
--- a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
+++ b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs
@@ -10,6 +10,7 @@ using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.Abstractions;
using OpenIddict.Core;
+using OpenIddict.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
@@ -86,7 +87,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));
@@ -143,7 +144,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));
@@ -200,7 +201,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));
@@ -257,7 +258,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));
@@ -314,7 +315,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));
@@ -411,7 +412,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));
@@ -507,7 +508,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));
@@ -604,7 +605,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 37e883cb..fcfe5273 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 73a0e076..044cfa11 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 79219a0d..9683733d 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 e435d37a..cfe05634 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 7d020653..eebd7503 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 8c8a1cdc..1a45afbd 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 1c650014..030a0f2c 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 3d6c1fc8..8964735e 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 ef419e8a..9c249b78 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 e009bee8..860ca846 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 bb160eee..00000000
--- a/src/OpenIddict.Server/Internal/OpenIddictServerEventService.cs
+++ /dev/null
@@ -1,163 +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.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.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/Internal/OpenIddictServerProvider.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
index 3570deef..bbc32d32 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;
@@ -28,7 +27,7 @@ namespace OpenIddict.Server.Internal
public sealed partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
private readonly ILogger _logger;
- private readonly OpenIddictServerEventService _eventService;
+ private readonly IOpenIddictServerEventService _eventService;
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly IOpenIddictScopeManager _scopeManager;
@@ -41,7 +40,7 @@ namespace OpenIddict.Server.Internal
///
public OpenIddictServerProvider(
[NotNull] ILogger logger,
- [NotNull] OpenIddictServerEventService eventService,
+ [NotNull] IOpenIddictServerEventService eventService,
[NotNull] IOpenIddictApplicationManager applicationManager,
[NotNull] IOpenIddictAuthorizationManager authorizationManager,
[NotNull] IOpenIddictScopeManager scopeManager,
diff --git a/src/OpenIddict.Server/OpenIddict.Server.csproj b/src/OpenIddict.Server/OpenIddict.Server.csproj
index 4decf8eb..3335441b 100644
--- a/src/OpenIddict.Server/OpenIddict.Server.csproj
+++ b/src/OpenIddict.Server/OpenIddict.Server.csproj
@@ -25,4 +25,8 @@
+
+
+
+
diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs
index 316f03cd..e49a735a 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;
@@ -20,6 +19,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
@@ -50,13 +50,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)
@@ -64,62 +64,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)
{
@@ -131,12 +99,21 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
- if (!typeof(IOpenIddictServerEventHandler).IsAssignableFrom(type))
+ if (type.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 bc9dd991..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,38 +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 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);
- }
+ 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..9873e49c
--- /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.SkipHandler(), 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.HandleResponse(),
+ /// context.SkipHandler(), context.Validate() or context.Reject()).
+ ///
+ Handled = 1
+ }
+}
diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
index 9c93193f..eeb3ca40 100644
--- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
@@ -42,7 +42,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddMemoryCache();
builder.Services.AddOptions();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
builder.Services.TryAddScoped(provider =>
{
@@ -54,7 +54,7 @@ namespace Microsoft.Extensions.DependencyInjection
return new OpenIddictServerProvider(
provider.GetRequiredService>(),
- provider.GetRequiredService(),
+ provider.GetRequiredService(),
provider.GetService() ?? throw CreateException(),
provider.GetService() ?? throw CreateException(),
provider.GetService() ?? throw CreateException(),
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 a686b661..00000000
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationEventService.cs
+++ /dev/null
@@ -1,76 +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.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.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/Internal/OpenIddictValidationProvider.cs b/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
index 3efd000a..a91e7697 100644
--- a/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
+++ b/src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
@@ -21,14 +21,14 @@ namespace OpenIddict.Validation.Internal
///
public sealed class OpenIddictValidationProvider : OAuthValidationEvents
{
- private readonly OpenIddictValidationEventService _eventService;
+ private readonly IOpenIddictValidationEventService _eventService;
///
/// 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 OpenIddictValidationProvider([NotNull] OpenIddictValidationEventService eventService)
+ public OpenIddictValidationProvider([NotNull] IOpenIddictValidationEventService eventService)
=> _eventService = eventService;
public override Task ApplyChallenge([NotNull] ApplyChallengeContext context)
diff --git a/src/OpenIddict.Validation/OpenIddict.Validation.csproj b/src/OpenIddict.Validation/OpenIddict.Validation.csproj
index 2a061224..5aef6f20 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 b1850ac7..568ee62e 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
@@ -7,11 +7,10 @@
using System;
using System.ComponentModel;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
-using OpenIddict.Abstractions;
+using OpenIddict.Extensions;
using OpenIddict.Validation;
namespace Microsoft.Extensions.DependencyInjection
@@ -42,13 +41,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 +55,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 +90,21 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
- if (!typeof(IOpenIddictValidationEventHandler).IsAssignableFrom(type))
+ if (type.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 c41f3e39..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,38 +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 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);
- }
+ 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 ae7d9bd2..202f4a9e 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
@@ -41,7 +41,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddLogging();
builder.Services.AddOptions();
- builder.Services.TryAddScoped();
+ builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
builder.Services.TryAddScoped();
diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
index c0689983..02bcb1b8 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
@@ -42,20 +42,18 @@ namespace OpenIddict.Server.Tests
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
- var handler = new OpenIddictServerEventHandler(
- (notification, cancellationToken) => Task.CompletedTask);
// 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 +62,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 +111,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]
@@ -873,10 +910,17 @@ namespace OpenIddict.Server.Tests
return options.Get(OpenIddictServerDefaults.AuthenticationScheme);
}
- 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 baa4f663..2924583a 100644
--- a/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
+++ b/test/OpenIddict.Server.Tests/OpenIddictServerExtensionsTests.cs
@@ -114,7 +114,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 e1738ff5..35d4926c 100644
--- a/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs
@@ -35,20 +35,18 @@ namespace OpenIddict.Validation.Tests
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
- var handler = new OpenIddictValidationEventHandler(
- (notification, cancellationToken) => Task.CompletedTask);
// 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 +55,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 +104,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]
@@ -205,10 +242,17 @@ namespace OpenIddict.Validation.Tests
return options.Get(OpenIddictValidationDefaults.AuthenticationScheme);
}
- 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 44702f99..d0f92f00 100644
--- a/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
+++ b/test/OpenIddict.Validation.Tests/OpenIddictValidationExtensionsTests.cs
@@ -97,7 +97,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));
}