Browse Source

Replace the application provider/events by a new notifications-based system

pull/623/head
Kévin Chalet 8 years ago
parent
commit
5c40217da4
  1. 2
      src/OpenIddict.Abstractions/OpenIddictBuilder.cs
  2. 2
      src/OpenIddict.Core/OpenIddictCoreBuilder.cs
  3. 7
      src/OpenIddict.Server/IOpenIddictServerEvent.cs
  4. 25
      src/OpenIddict.Server/IOpenIddictServerEventHandler.cs
  5. 22
      src/OpenIddict.Server/IOpenIddictServerEventService.cs
  6. 36
      src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs
  7. 13
      src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs
  8. 10
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
  9. 25
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs
  10. 14
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs
  11. 2
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs
  12. 12
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs
  13. 12
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs
  14. 20
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs
  15. 10
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs
  16. 13
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs
  17. 78
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs
  18. 158
      src/OpenIddict.Server/OpenIddictServerBuilder.cs
  19. 24
      src/OpenIddict.Server/OpenIddictServerEvent.cs
  20. 49
      src/OpenIddict.Server/OpenIddictServerEventHandler.cs
  21. 150
      src/OpenIddict.Server/OpenIddictServerEventService.cs
  22. 565
      src/OpenIddict.Server/OpenIddictServerEvents.cs
  23. 2
      src/OpenIddict.Server/OpenIddictServerExtensions.cs
  24. 14
      src/OpenIddict.Server/OpenIddictServerOptions.cs
  25. 7
      src/OpenIddict.Validation/IOpenIddictValidationEvent.cs
  26. 25
      src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs
  27. 22
      src/OpenIddict.Validation/IOpenIddictValidationEventService.cs
  28. 34
      src/OpenIddict.Validation/Internal/OpenIddictValidationHandler.cs
  29. 14
      src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs
  30. 28
      src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs
  31. 136
      src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
  32. 24
      src/OpenIddict.Validation/OpenIddictValidationEvent.cs
  33. 49
      src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs
  34. 63
      src/OpenIddict.Validation/OpenIddictValidationEventService.cs
  35. 71
      src/OpenIddict.Validation/OpenIddictValidationEvents.cs
  36. 3
      src/OpenIddict.Validation/OpenIddictValidationExtensions.cs
  37. 17
      src/OpenIddict.Validation/OpenIddictValidationOptions.cs
  38. 47
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs
  39. 125
      test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs
  40. 110
      test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs
  41. 2
      test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationProviderTests.cs
  42. 79
      test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs

2
src/OpenIddict.Abstractions/OpenIddictBuilder.cs

@ -35,4 +35,4 @@ namespace Microsoft.Extensions.DependencyInjection
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
}
}
}

2
src/OpenIddict.Core/OpenIddictCoreBuilder.cs

@ -779,4 +779,4 @@ namespace Microsoft.Extensions.DependencyInjection
return Configure(options => options.DefaultTokenType = type);
}
}
}
}

7
src/OpenIddict.Server/IOpenIddictServerEvent.cs

@ -0,0 +1,7 @@
namespace OpenIddict.Server
{
/// <summary>
/// Represents an OpenIddict server event.
/// </summary>
public interface IOpenIddictServerEvent { }
}

25
src/OpenIddict.Server/IOpenIddictServerEventHandler.cs

@ -0,0 +1,25 @@
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Server
{
/// <summary>
/// Represents a handler able to process <typeparamref name="TEvent"/> events.
/// </summary>
/// <typeparam name="TEvent">The type of the events handled by this instance.</typeparam>
public interface IOpenIddictServerEventHandler<TEvent> where TEvent : class, IOpenIddictServerEvent
{
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
}
}

22
src/OpenIddict.Server/IOpenIddictServerEventService.cs

@ -0,0 +1,22 @@
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Server
{
/// <summary>
/// Dispatches events by invoking the corresponding handlers.
/// </summary>
public interface IOpenIddictServerEventService
{
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task PublishAsync<TEvent>([NotNull] TEvent notification, CancellationToken cancellationToken = default)
where TEvent : class, IOpenIddictServerEvent;
}
}

36
src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs

@ -1,8 +1,5 @@
using System;
using System.ComponentModel;
using System.Text;
using System.ComponentModel;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
@ -22,36 +19,5 @@ namespace OpenIddict.Server
: base(options, logger, encoder, clock)
{
}
protected override async Task InitializeEventsAsync()
{
await base.InitializeEventsAsync();
// If an application provider instance or type was specified, import the application provider events.
if (Options.ApplicationProvider != null || Options.ApplicationProviderType != null)
{
// Resolve the user provider from the options or from the services container.
var provider = Options.ApplicationProvider;
if (provider == null)
{
provider = Context.RequestServices.GetService(Options.ApplicationProviderType) as OpenIdConnectServerProvider;
}
if (provider == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The application provider cannot be resolved from the dependency injection container. ")
.Append("Make sure it is correctly registered in 'ConfigureServices(IServiceCollection services)'.")
.ToString());
}
// Update the main provider to invoke the user provider's event handlers.
Provider.Import(provider);
}
}
private new OpenIddictServerOptions Options => (OpenIddictServerOptions) base.Options;
private OpenIddictServerProvider Provider => (OpenIddictServerProvider) base.Events;
}
}

13
src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs

@ -63,19 +63,6 @@ namespace OpenIddict.Server
throw new InvalidOperationException("A random number generator must be registered.");
}
if (options.ApplicationProviderType != null)
{
if (options.ApplicationProvider != null)
{
throw new InvalidOperationException("An application provider cannot be registered when a type is specified.");
}
if (!typeof(OpenIdConnectServerProvider).IsAssignableFrom(options.ApplicationProviderType))
{
throw new InvalidOperationException("Application providers must inherit from OpenIdConnectServerProvider.");
}
}
// When no distributed cache has been registered in the options,
// try to resolve it from the dependency injection container.
if (options.Cache == null)

10
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs

@ -107,7 +107,7 @@ namespace OpenIddict.Server
}
}
await base.ExtractAuthorizationRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ExtractAuthorizationRequest(context));
}
public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context)
@ -433,7 +433,7 @@ namespace OpenIddict.Server
context.Validate();
await base.ValidateAuthorizationRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateAuthorizationRequest(context));
}
public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context)
@ -485,7 +485,7 @@ namespace OpenIddict.Server
return;
}
await base.HandleAuthorizationRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.HandleAuthorizationRequest(context));
}
public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context)
@ -526,7 +526,7 @@ namespace OpenIddict.Server
}
}
await base.ApplyAuthorizationResponse(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ApplyAuthorizationResponse(context));
}
}
}
}

25
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs

@ -13,6 +13,12 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
public override Task ExtractConfigurationRequest([NotNull] ExtractConfigurationRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ExtractConfigurationRequest(context));
public override Task ValidateConfigurationRequest([NotNull] ValidateConfigurationRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ValidateConfigurationRequest(context));
public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@ -41,7 +47,22 @@ namespace OpenIddict.Server
context.Metadata[OpenIdConnectConstants.Metadata.RequestParameterSupported] = false;
context.Metadata[OpenIdConnectConstants.Metadata.RequestUriParameterSupported] = false;
return base.HandleConfigurationRequest(context);
return _eventService.PublishAsync(new OpenIddictServerEvents.HandleConfigurationRequest(context));
}
public override Task ApplyConfigurationResponse([NotNull] ApplyConfigurationResponseContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ApplyConfigurationResponse(context));
public override Task ExtractCryptographyRequest([NotNull] ExtractCryptographyRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ExtractCryptographyRequest(context));
public override Task ValidateCryptographyRequest([NotNull] ValidateCryptographyRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ValidateCryptographyRequest(context));
public override Task HandleCryptographyRequest([NotNull] HandleCryptographyRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.HandleCryptographyRequest(context));
public override Task ApplyCryptographyResponse([NotNull] ApplyCryptographyResponseContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ApplyCryptographyResponse(context));
}
}
}

14
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs

@ -20,6 +20,9 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
public override Task ExtractTokenRequest([NotNull] ExtractTokenRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ExtractTokenRequest(context));
public override async Task ValidateTokenRequest([NotNull] ValidateTokenRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@ -289,7 +292,7 @@ namespace OpenIddict.Server
context.Validate();
await base.ValidateTokenRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateTokenRequest(context));
}
public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context)
@ -308,7 +311,7 @@ namespace OpenIddict.Server
// the user code to handle the token request.
context.SkipHandler();
await base.HandleTokenRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.HandleTokenRequest(context));
return;
}
@ -400,7 +403,10 @@ namespace OpenIddict.Server
// the user code to handle the token request.
context.SkipHandler();
await base.HandleTokenRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.HandleTokenRequest(context));
}
public override Task ApplyTokenResponse([NotNull] ApplyTokenResponseContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ApplyTokenResponse(context));
}
}
}

2
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs

@ -648,4 +648,4 @@ namespace OpenIddict.Server
}
}
}
}
}

12
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs

@ -17,6 +17,9 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
public override Task ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ExtractIntrospectionRequest(context));
public override async Task ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@ -94,7 +97,7 @@ namespace OpenIddict.Server
context.Validate();
await base.ValidateIntrospectionRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateIntrospectionRequest(context));
}
public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context)
@ -177,7 +180,10 @@ namespace OpenIddict.Server
}
}
await base.HandleIntrospectionRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.HandleIntrospectionRequest(context));
}
public override Task ApplyIntrospectionResponse([NotNull] ApplyIntrospectionResponseContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ApplyIntrospectionResponse(context));
}
}
}

12
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs

@ -18,6 +18,9 @@ namespace OpenIddict.Server
{
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
public override Task ExtractRevocationRequest([NotNull] ExtractRevocationRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ExtractRevocationRequest(context));
public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@ -160,7 +163,7 @@ namespace OpenIddict.Server
context.Validate();
await base.ValidateRevocationRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateRevocationRequest(context));
}
public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context)
@ -232,7 +235,10 @@ namespace OpenIddict.Server
context.Revoked = true;
await base.HandleRevocationRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.HandleRevocationRequest(context));
}
public override Task ApplyRevocationResponse([NotNull] ApplyRevocationResponseContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ApplyRevocationResponse(context));
}
}
}

20
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs

@ -34,7 +34,7 @@ namespace OpenIddict.Server
context.HandleDeserialization();
}
await base.DeserializeAccessToken(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeAccessToken(context));
}
public override async Task DeserializeAuthorizationCode([NotNull] DeserializeAuthorizationCodeContext context)
@ -53,9 +53,12 @@ namespace OpenIddict.Server
// Prevent the OpenID Connect server middleware from using its default logic.
context.HandleDeserialization();
await base.DeserializeAuthorizationCode(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeAuthorizationCode(context));
}
public override Task DeserializeIdentityToken(DeserializeIdentityTokenContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeIdentityToken(context));
public override async Task DeserializeRefreshToken([NotNull] DeserializeRefreshTokenContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@ -72,7 +75,7 @@ namespace OpenIddict.Server
// Prevent the OpenID Connect server middleware from using its default logic.
context.HandleDeserialization();
await base.DeserializeRefreshToken(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.DeserializeRefreshToken(context));
}
public override async Task SerializeAccessToken([NotNull] SerializeAccessTokenContext context)
@ -98,7 +101,7 @@ namespace OpenIddict.Server
// Otherwise, let the OpenID Connect server middleware
// serialize the token using its default internal logic.
await base.SerializeAccessToken(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.SerializeAccessToken(context));
}
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context)
@ -126,9 +129,12 @@ namespace OpenIddict.Server
// Otherwise, let the OpenID Connect server middleware
// serialize the token using its default internal logic.
await base.SerializeAuthorizationCode(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.SerializeAuthorizationCode(context));
}
public override Task SerializeIdentityToken(SerializeIdentityTokenContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.SerializeIdentityToken(context));
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context)
{
var options = (OpenIddictServerOptions) context.Options;
@ -154,7 +160,7 @@ namespace OpenIddict.Server
// Otherwise, let the OpenID Connect server middleware
// serialize the token using its default internal logic.
await base.SerializeRefreshToken(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.SerializeRefreshToken(context));
}
}
}
}

10
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs

@ -77,7 +77,7 @@ namespace OpenIddict.Server
}
}
await base.ExtractLogoutRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ExtractLogoutRequest(context));
}
public override async Task ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context)
@ -151,7 +151,7 @@ namespace OpenIddict.Server
context.Validate();
await base.ValidateLogoutRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateLogoutRequest(context));
}
public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context)
@ -203,7 +203,7 @@ namespace OpenIddict.Server
return;
}
await base.HandleLogoutRequest(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.HandleLogoutRequest(context));
}
public override async Task ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context)
@ -244,7 +244,7 @@ namespace OpenIddict.Server
}
}
await base.ApplyLogoutResponse(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ApplyLogoutResponse(context));
}
}
}
}

13
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs

@ -24,7 +24,16 @@ namespace OpenIddict.Server
// the user code to handle the userinfo request.
context.SkipHandler();
return base.ExtractUserinfoRequest(context);
return _eventService.PublishAsync(new OpenIddictServerEvents.ExtractUserinfoRequest(context));
}
public override Task ValidateUserinfoRequest([NotNull] ValidateUserinfoRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ValidateUserinfoRequest(context));
public override Task HandleUserinfoRequest([NotNull] HandleUserinfoRequestContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.HandleUserinfoRequest(context));
public override Task ApplyUserinfoResponse([NotNull] ApplyUserinfoResponseContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.ApplyUserinfoResponse(context));
}
}
}

78
src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs

@ -24,26 +24,32 @@ namespace OpenIddict.Server
[EditorBrowsable(EditorBrowsableState.Never)]
public partial class OpenIddictServerProvider : OpenIdConnectServerProvider
{
public readonly ILogger _logger;
public readonly IOpenIddictApplicationManager _applicationManager;
public readonly IOpenIddictAuthorizationManager _authorizationManager;
public readonly IOpenIddictScopeManager _scopeManager;
public readonly IOpenIddictTokenManager _tokenManager;
private readonly ILogger _logger;
private readonly IOpenIddictServerEventService _eventService;
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly IOpenIddictTokenManager _tokenManager;
public OpenIddictServerProvider(
[NotNull] ILogger<OpenIddictServerProvider> logger,
[NotNull] IOpenIddictServerEventService eventService,
[NotNull] IOpenIddictApplicationManager applicationManager,
[NotNull] IOpenIddictAuthorizationManager authorizationManager,
[NotNull] IOpenIddictScopeManager scopeManager,
[NotNull] IOpenIddictTokenManager tokenManager)
{
_logger = logger;
_eventService = eventService;
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_scopeManager = scopeManager;
_tokenManager = tokenManager;
}
public override Task MatchEndpoint([NotNull] MatchEndpointContext context)
=> _eventService.PublishAsync(new OpenIddictServerEvents.MatchEndpoint(context));
public override Task ProcessChallengeResponse([NotNull] ProcessChallengeResponseContext context)
{
Debug.Assert(context.Request.IsAuthorizationRequest() ||
@ -58,7 +64,7 @@ namespace OpenIddict.Server
context.Response.AddParameter(parameter, value);
}
return base.ProcessChallengeResponse(context);
return _eventService.PublishAsync(new OpenIddictServerEvents.ProcessChallengeResponse(context));
}
public override async Task ProcessSigninResponse([NotNull] ProcessSigninResponseContext context)
@ -188,7 +194,7 @@ namespace OpenIddict.Server
context.Ticket.RemoveProperty(property);
}
await base.ProcessSigninResponse(context);
await _eventService.PublishAsync(new OpenIddictServerEvents.ProcessSigninResponse(context));
}
public override Task ProcessSignoutResponse([NotNull] ProcessSignoutResponseContext context)
@ -202,61 +208,7 @@ namespace OpenIddict.Server
context.Response.AddParameter(parameter, value);
}
return base.ProcessSignoutResponse(context);
}
public void Import([NotNull] OpenIdConnectServerProvider provider)
{
OnMatchEndpoint = provider.MatchEndpoint;
OnExtractAuthorizationRequest = provider.ExtractAuthorizationRequest;
OnExtractConfigurationRequest = provider.ExtractConfigurationRequest;
OnExtractCryptographyRequest = provider.ExtractCryptographyRequest;
OnExtractIntrospectionRequest = provider.ExtractIntrospectionRequest;
OnExtractLogoutRequest = provider.ExtractLogoutRequest;
OnExtractRevocationRequest = provider.ExtractRevocationRequest;
OnExtractTokenRequest = provider.ExtractTokenRequest;
OnExtractUserinfoRequest = provider.ExtractUserinfoRequest;
OnValidateAuthorizationRequest = provider.ValidateAuthorizationRequest;
OnValidateConfigurationRequest = provider.ValidateConfigurationRequest;
OnValidateCryptographyRequest = provider.ValidateCryptographyRequest;
OnValidateIntrospectionRequest = provider.ValidateIntrospectionRequest;
OnValidateLogoutRequest = provider.ValidateLogoutRequest;
OnValidateRevocationRequest = provider.ValidateRevocationRequest;
OnValidateTokenRequest = provider.ValidateTokenRequest;
OnValidateUserinfoRequest = provider.ValidateUserinfoRequest;
OnHandleAuthorizationRequest = provider.HandleAuthorizationRequest;
OnHandleConfigurationRequest = provider.HandleConfigurationRequest;
OnHandleCryptographyRequest = provider.HandleCryptographyRequest;
OnHandleIntrospectionRequest = provider.HandleIntrospectionRequest;
OnHandleLogoutRequest = provider.HandleLogoutRequest;
OnHandleRevocationRequest = provider.HandleRevocationRequest;
OnHandleTokenRequest = provider.HandleTokenRequest;
OnHandleUserinfoRequest = provider.HandleUserinfoRequest;
OnApplyAuthorizationResponse = provider.ApplyAuthorizationResponse;
OnApplyConfigurationResponse = provider.ApplyConfigurationResponse;
OnApplyCryptographyResponse = provider.ApplyCryptographyResponse;
OnApplyIntrospectionResponse = provider.ApplyIntrospectionResponse;
OnApplyLogoutResponse = provider.ApplyLogoutResponse;
OnApplyRevocationResponse = provider.ApplyRevocationResponse;
OnApplyTokenResponse = provider.ApplyTokenResponse;
OnApplyUserinfoResponse = provider.ApplyUserinfoResponse;
OnProcessChallengeResponse = provider.ProcessChallengeResponse;
OnProcessSigninResponse = provider.ProcessSigninResponse;
OnProcessSignoutResponse = provider.ProcessSignoutResponse;
OnDeserializeAccessToken = provider.DeserializeAccessToken;
OnDeserializeAuthorizationCode = provider.DeserializeAuthorizationCode;
OnDeserializeIdentityToken = provider.DeserializeIdentityToken;
OnDeserializeRefreshToken = provider.DeserializeRefreshToken;
OnSerializeAccessToken = provider.SerializeAccessToken;
OnSerializeAuthorizationCode = provider.SerializeAuthorizationCode;
OnSerializeIdentityToken = provider.SerializeIdentityToken;
OnSerializeRefreshToken = provider.SerializeRefreshToken;
return _eventService.PublishAsync(new OpenIddictServerEvents.ProcessSignoutResponse(context));
}
}
}
}

158
src/OpenIddict.Server/OpenIddictServerBuilder.cs

@ -12,12 +12,12 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Server;
@ -48,6 +48,98 @@ namespace Microsoft.Extensions.DependencyInjection
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>(
[NotNull] IOpenIddictServerEventHandler<TEvent> handler)
where TEvent : class, IOpenIddictServerEvent
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
Services.AddSingleton(handler);
return this;
}
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, Task> handler)
where TEvent : class, IOpenIddictServerEvent
=> AddEventHandler<TEvent>((notification, cancellationToken) => handler(notification));
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, CancellationToken, Task> handler)
where TEvent : class, IOpenIddictServerEvent
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}
return AddEventHandler(new OpenIddictServerEventHandler<TEvent>(handler));
}
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <typeparam name="TEvent">The type of the event.</typeparam>
/// <typeparam name="THandler">The type of the handler.</typeparam>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent, THandler>(
ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictServerEvent
where THandler : IOpenIddictServerEventHandler<TEvent>
=> AddEventHandler<TEvent>(typeof(THandler));
/// <summary>
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="type">The type of the handler.</param>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder AddEventHandler<TEvent>(
[NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictServerEvent
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (lifetime == ServiceLifetime.Transient)
{
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
if (!typeof(IOpenIddictServerEventHandler<TEvent>).IsAssignableFrom(type))
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
Services.Add(new ServiceDescriptor(typeof(IOpenIddictServerEventHandler<TEvent>), type, lifetime));
return this;
}
/// <summary>
/// Amends the default OpenIddict server configuration.
/// </summary>
@ -184,7 +276,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
throw new ArgumentException("The password cannot be null or empty.", nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(assembly, resource, password));
@ -206,7 +298,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
throw new ArgumentException("The password cannot be null or empty.", nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(stream, password));
@ -233,7 +325,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
throw new ArgumentException("The password cannot be null or empty.", nameof(password));
}
return Configure(options => options.SigningCredentials.AddCertificate(stream, password, flags));
@ -552,60 +644,6 @@ namespace Microsoft.Extensions.DependencyInjection
return Configure(options => options.Claims.UnionWith(claims));
}
/// <summary>
/// Registers an application-specific OpenID Connect server provider whose events
/// are automatically invoked for each request handled by the OpenIddict server handler.
/// Using this method is NOT recommended if you're not familiar with the OIDC events model.
/// </summary>
/// <param name="provider">The custom <see cref="OpenIdConnectServerProvider"/> service.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder RegisterProvider([NotNull] OpenIdConnectServerProvider provider)
{
if (provider == null)
{
throw new ArgumentNullException(nameof(provider));
}
return Configure(options => options.ApplicationProvider = provider);
}
/// <summary>
/// Registers an application-specific OpenID Connect server provider whose events
/// are automatically invoked for each request handled by the OpenIddict server handler.
/// Using this method is NOT recommended if you're not familiar with the OIDC events model.
/// </summary>
/// <typeparam name="TProvider">The type of the custom <see cref="OpenIdConnectServerProvider"/> service.</typeparam>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder RegisterProvider<TProvider>() where TProvider : OpenIdConnectServerProvider
=> RegisterProvider(typeof(TProvider));
/// <summary>
/// Registers an application-specific OpenID Connect server provider whose events
/// are automatically invoked for each request handled by the OpenIddict server handler.
/// Using this method is NOT recommended if you're not familiar with the OIDC events model.
/// </summary>
/// <param name="type">The type of the custom <see cref="OpenIdConnectServerProvider"/> service.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictServerBuilder RegisterProvider([NotNull] Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (!typeof(OpenIdConnectServerProvider).IsAssignableFrom(type))
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
Services.TryAddScoped(type);
return Configure(options => options.ApplicationProviderType = type);
}
/// <summary>
/// Registers the specified scopes as supported scopes so
/// they can be returned as part of the discovery document.
@ -735,4 +773,4 @@ namespace Microsoft.Extensions.DependencyInjection
public OpenIddictServerBuilder UseRollingTokens()
=> Configure(options => options.UseRollingTokens = true);
}
}
}

24
src/OpenIddict.Server/OpenIddictServerEvent.cs

@ -0,0 +1,24 @@
using System;
using JetBrains.Annotations;
namespace OpenIddict.Server
{
/// <summary>
/// Represents an OpenIddict server event.
/// </summary>
/// <typeparam name="TContext">The type of the context instance associated with the event.</typeparam>
public class OpenIddictServerEvent<TContext> : IOpenIddictServerEvent where TContext : class
{
/// <summary>
/// Creates a new instance of <see cref="OpenIddictServerEvent{TContext}"/>.
/// </summary>
/// <param name="context">The context instance associated with the event.</param>
public OpenIddictServerEvent([NotNull] TContext context)
=> Context = context ?? throw new ArgumentNullException(nameof(context));
/// <summary>
/// Gets the context instance associated with the event.
/// </summary>
public TContext Context { get; }
}
}

49
src/OpenIddict.Server/OpenIddictServerEventHandler.cs

@ -0,0 +1,49 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Server
{
/// <summary>
/// Represents a handler able to process <typeparamref name="TEvent"/> events.
/// </summary>
/// <typeparam name="TEvent">The type of the events handled by this instance.</typeparam>
public class OpenIddictServerEventHandler<TEvent> : IOpenIddictServerEventHandler<TEvent>
where TEvent : class, IOpenIddictServerEvent
{
private readonly Func<TEvent, CancellationToken, Task> _handler;
/// <summary>
/// Creates a new event using the specified handler delegate.
/// </summary>
/// <param name="handler">The event handler delegate</param>
public OpenIddictServerEventHandler([NotNull] Func<TEvent, CancellationToken, Task> handler)
=> _handler = handler ?? throw new ArgumentNullException(nameof(handler));
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
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);
}
}
}

150
src/OpenIddict.Server/OpenIddictServerEventService.cs

@ -0,0 +1,150 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server
{
/// <summary>
/// Dispatches notifications by invoking the corresponding handlers.
/// </summary>
public class OpenIddictServerEventService : IOpenIddictServerEventService
{
private readonly IServiceProvider _provider;
public OpenIddictServerEventService([NotNull] IServiceProvider provider)
{
_provider = provider;
}
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public async Task PublishAsync<TEvent>([NotNull] TEvent notification, CancellationToken cancellationToken = default)
where TEvent : class, IOpenIddictServerEvent
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
foreach (var handler in _provider.GetServices<IOpenIddictServerEventHandler<TEvent>>())
{
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;
}
}
}
}
}

565
src/OpenIddict.Server/OpenIddictServerEvents.cs

@ -0,0 +1,565 @@
using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
namespace OpenIddict.Server
{
/// <summary>
/// Contains common events used by the OpenIddict server handler.
/// </summary>
public static class OpenIddictServerEvents
{
/// <summary>
/// Represents an event called for each HTTP request to determine if
/// it should be handled by the OpenID Connect server middleware.
/// </summary>
public sealed class MatchEndpoint : OpenIddictServerEvent<MatchEndpointContext>
{
/// <summary>
/// Creates a new instance of <see cref="MatchEndpoint"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public MatchEndpoint([NotNull] MatchEndpointContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the authorization endpoint to give the user code
/// a chance to manually extract the authorization request from the ambient HTTP context.
/// </summary>
public sealed class ExtractAuthorizationRequest : OpenIddictServerEvent<ExtractAuthorizationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractAuthorizationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractAuthorizationRequest([NotNull] ExtractAuthorizationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the configuration endpoint to give the user code
/// a chance to manually extract the configuration request from the ambient HTTP context.
/// </summary>
public sealed class ExtractConfigurationRequest : OpenIddictServerEvent<ExtractConfigurationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractConfigurationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractConfigurationRequest([NotNull] ExtractConfigurationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the cryptography endpoint to give the user code
/// a chance to manually extract the cryptography request from the ambient HTTP context.
/// </summary>
public sealed class ExtractCryptographyRequest : OpenIddictServerEvent<ExtractCryptographyRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractCryptographyRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractCryptographyRequest([NotNull] ExtractCryptographyRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the introspection endpoint to give the user code
/// a chance to manually extract the introspection request from the ambient HTTP context.
/// </summary>
public sealed class ExtractIntrospectionRequest : OpenIddictServerEvent<ExtractIntrospectionRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractIntrospectionRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the logout endpoint to give the user code
/// a chance to manually extract the logout request from the ambient HTTP context.
/// </summary>
public sealed class ExtractLogoutRequest : OpenIddictServerEvent<ExtractLogoutRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractLogoutRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractLogoutRequest([NotNull] ExtractLogoutRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the revocation endpoint to give the user code
/// a chance to manually extract the revocation request from the ambient HTTP context.
/// </summary>
public sealed class ExtractRevocationRequest : OpenIddictServerEvent<ExtractRevocationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractRevocationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractRevocationRequest([NotNull] ExtractRevocationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the token endpoint to give the user code
/// a chance to manually extract the token request from the ambient HTTP context.
/// </summary>
public sealed class ExtractTokenRequest : OpenIddictServerEvent<ExtractTokenRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractTokenRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractTokenRequest([NotNull] ExtractTokenRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the userinfo endpoint to give the user code
/// a chance to manually extract the userinfo request from the ambient HTTP context.
/// </summary>
public sealed class ExtractUserinfoRequest : OpenIddictServerEvent<ExtractUserinfoRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ExtractUserinfoRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ExtractUserinfoRequest([NotNull] ExtractUserinfoRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the authorization endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateAuthorizationRequest : OpenIddictServerEvent<ValidateAuthorizationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateAuthorizationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the configuration endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateConfigurationRequest : OpenIddictServerEvent<ValidateConfigurationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateConfigurationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateConfigurationRequest([NotNull] ValidateConfigurationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the cryptography endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateCryptographyRequest : OpenIddictServerEvent<ValidateCryptographyRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateCryptographyRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateCryptographyRequest([NotNull] ValidateCryptographyRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the introspection endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateIntrospectionRequest : OpenIddictServerEvent<ValidateIntrospectionRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateIntrospectionRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateIntrospectionRequest([NotNull] ValidateIntrospectionRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the logout endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateLogoutRequest : OpenIddictServerEvent<ValidateLogoutRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateLogoutRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateLogoutRequest([NotNull] ValidateLogoutRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the revocation endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateRevocationRequest : OpenIddictServerEvent<ValidateRevocationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateRevocationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the token endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateTokenRequest : OpenIddictServerEvent<ValidateTokenRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateTokenRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateTokenRequest([NotNull] ValidateTokenRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each request to the userinfo endpoint
/// to determine if the request is valid and should continue to be processed.
/// </summary>
public sealed class ValidateUserinfoRequest : OpenIddictServerEvent<ValidateUserinfoRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateUserinfoRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateUserinfoRequest([NotNull] ValidateUserinfoRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated authorization request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleAuthorizationRequest : OpenIddictServerEvent<HandleAuthorizationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleAuthorizationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated configuration request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleConfigurationRequest : OpenIddictServerEvent<HandleConfigurationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleConfigurationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated cryptography request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleCryptographyRequest : OpenIddictServerEvent<HandleCryptographyRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleCryptographyRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleCryptographyRequest([NotNull] HandleCryptographyRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated introspection request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleIntrospectionRequest : OpenIddictServerEvent<HandleIntrospectionRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleIntrospectionRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated logout request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleLogoutRequest : OpenIddictServerEvent<HandleLogoutRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleLogoutRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleLogoutRequest([NotNull] HandleLogoutRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated revocation request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleRevocationRequest : OpenIddictServerEvent<HandleRevocationRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleRevocationRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleRevocationRequest([NotNull] HandleRevocationRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated token request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleTokenRequest : OpenIddictServerEvent<HandleTokenRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleTokenRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleTokenRequest([NotNull] HandleTokenRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called for each validated userinfo request
/// to allow the user code to decide how the request should be handled.
/// </summary>
public sealed class HandleUserinfoRequest : OpenIddictServerEvent<HandleUserinfoRequestContext>
{
/// <summary>
/// Creates a new instance of <see cref="HandleUserinfoRequest"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public HandleUserinfoRequest([NotNull] HandleUserinfoRequestContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when processing a challenge response.
/// </summary>
public sealed class ProcessChallengeResponse : OpenIddictServerEvent<ProcessChallengeResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ProcessChallengeResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ProcessChallengeResponse([NotNull] ProcessChallengeResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when processing a sign-in response.
/// </summary>
public sealed class ProcessSigninResponse : OpenIddictServerEvent<ProcessSigninResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ProcessSigninResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ProcessSigninResponse([NotNull] ProcessSigninResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when processing a sign-out response.
/// </summary>
public sealed class ProcessSignoutResponse : OpenIddictServerEvent<ProcessSignoutResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ProcessSignoutResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ProcessSignoutResponse([NotNull] ProcessSignoutResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the authorization response is returned to the caller.
/// </summary>
public sealed class ApplyAuthorizationResponse : OpenIddictServerEvent<ApplyAuthorizationResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyAuthorizationResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the configuration response is returned to the caller.
/// </summary>
public sealed class ApplyConfigurationResponse : OpenIddictServerEvent<ApplyConfigurationResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyConfigurationResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyConfigurationResponse([NotNull] ApplyConfigurationResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the cryptography response is returned to the caller.
/// </summary>
public sealed class ApplyCryptographyResponse : OpenIddictServerEvent<ApplyCryptographyResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyCryptographyResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyCryptographyResponse([NotNull] ApplyCryptographyResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the introspection response is returned to the caller.
/// </summary>
public sealed class ApplyIntrospectionResponse : OpenIddictServerEvent<ApplyIntrospectionResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyIntrospectionResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyIntrospectionResponse([NotNull] ApplyIntrospectionResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the logout response is returned to the caller.
/// </summary>
public sealed class ApplyLogoutResponse : OpenIddictServerEvent<ApplyLogoutResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyLogoutResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the revocation response is returned to the caller.
/// </summary>
public sealed class ApplyRevocationResponse : OpenIddictServerEvent<ApplyRevocationResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyRevocationResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyRevocationResponse([NotNull] ApplyRevocationResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the token response is returned to the caller.
/// </summary>
public sealed class ApplyTokenResponse : OpenIddictServerEvent<ApplyTokenResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyTokenResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyTokenResponse([NotNull] ApplyTokenResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called before the userinfo response is returned to the caller.
/// </summary>
public sealed class ApplyUserinfoResponse : OpenIddictServerEvent<ApplyUserinfoResponseContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyUserinfoResponse"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyUserinfoResponse([NotNull] ApplyUserinfoResponseContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when serializing an authorization code.
/// </summary>
public sealed class SerializeAuthorizationCode : OpenIddictServerEvent<SerializeAuthorizationCodeContext>
{
/// <summary>
/// Creates a new instance of <see cref="SerializeAuthorizationCode"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when serializing an access token.
/// </summary>
public sealed class SerializeAccessToken : OpenIddictServerEvent<SerializeAccessTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="SerializeAccessToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public SerializeAccessToken([NotNull] SerializeAccessTokenContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when serializing an identity token.
/// </summary>
public sealed class SerializeIdentityToken : OpenIddictServerEvent<SerializeIdentityTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="SerializeIdentityToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public SerializeIdentityToken([NotNull] SerializeIdentityTokenContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when serializing a refresh token.
/// </summary>
public sealed class SerializeRefreshToken : OpenIddictServerEvent<SerializeRefreshTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="SerializeRefreshToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when deserializing an authorization code.
/// </summary>
public sealed class DeserializeAuthorizationCode : OpenIddictServerEvent<DeserializeAuthorizationCodeContext>
{
/// <summary>
/// Creates a new instance of <see cref="DeserializeAuthorizationCode"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public DeserializeAuthorizationCode([NotNull] DeserializeAuthorizationCodeContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when deserializing an access token.
/// </summary>
public sealed class DeserializeAccessToken : OpenIddictServerEvent<DeserializeAccessTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="DeserializeAccessToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public DeserializeAccessToken([NotNull] DeserializeAccessTokenContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when deserializing an identity token.
/// </summary>
public sealed class DeserializeIdentityToken : OpenIddictServerEvent<DeserializeIdentityTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="DeserializeIdentityToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public DeserializeIdentityToken([NotNull] DeserializeIdentityTokenContext context) : base(context) { }
}
/// <summary>
/// Represents an event called when deserializing a refresh token.
/// </summary>
public sealed class DeserializeRefreshToken : OpenIddictServerEvent<DeserializeRefreshTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="DeserializeRefreshToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public DeserializeRefreshToken([NotNull] DeserializeRefreshTokenContext context) : base(context) { }
}
}
}

2
src/OpenIddict.Server/OpenIddictServerExtensions.cs

@ -34,6 +34,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddAuthentication();
builder.Services.TryAddScoped<IOpenIddictServerEventService, OpenIddictServerEventService>();
builder.Services.TryAddScoped<OpenIddictServerHandler>();
builder.Services.TryAddScoped(provider =>
{
@ -44,6 +45,7 @@ namespace Microsoft.Extensions.DependencyInjection
return new OpenIddictServerProvider(
provider.GetRequiredService<ILogger<OpenIddictServerProvider>>(),
provider.GetRequiredService<IOpenIddictServerEventService>(),
provider.GetService<IOpenIddictApplicationManager>() ?? throw CreateException(),
provider.GetService<IOpenIddictAuthorizationManager>() ?? throw CreateException(),
provider.GetService<IOpenIddictScopeManager>() ?? throw CreateException(),

14
src/OpenIddict.Server/OpenIddictServerOptions.cs

@ -34,20 +34,6 @@ namespace OpenIddict.Server
/// </summary>
public bool AcceptAnonymousClients { get; set; }
/// <summary>
/// Gets or sets the user-provided <see cref="OpenIdConnectServerProvider"/> that the OpenIddict server
/// invokes to enable developer control over the entire authentication/authorization process.
/// </summary>
public OpenIdConnectServerProvider ApplicationProvider { get; set; }
/// <summary>
/// Gets or sets the user-provided provider type that the OpenIddict server handler instantiates
/// to enable developer control over the entire authentication/authorization process. When this
/// property is set, the provider is resolved from the services container. If the provider is not
/// guaranteed to be thread-safe, registering it as a scoped dependency is strongly recommended.
/// </summary>
public Type ApplicationProviderType { get; set; }
/// <summary>
/// Gets or sets the distributed cache used by OpenIddict. If no cache is explicitly
/// provided, the cache registered in the dependency injection container is used.

7
src/OpenIddict.Validation/IOpenIddictValidationEvent.cs

@ -0,0 +1,7 @@
namespace OpenIddict.Validation
{
/// <summary>
/// Represents an OpenIddict validation event.
/// </summary>
public interface IOpenIddictValidationEvent { }
}

25
src/OpenIddict.Validation/IOpenIddictValidationEventHandler.cs

@ -0,0 +1,25 @@
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Validation
{
/// <summary>
/// Represents a handler able to process <typeparamref name="TEvent"/> events.
/// </summary>
/// <typeparam name="TEvent">The type of the events handled by this instance.</typeparam>
public interface IOpenIddictValidationEventHandler<TEvent> where TEvent : class, IOpenIddictValidationEvent
{
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task HandleAsync([NotNull] TEvent notification, CancellationToken cancellationToken);
}
}

22
src/OpenIddict.Validation/IOpenIddictValidationEventService.cs

@ -0,0 +1,22 @@
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Validation
{
/// <summary>
/// Dispatches events by invoking the corresponding handlers.
/// </summary>
public interface IOpenIddictValidationEventService
{
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
Task PublishAsync<TEvent>([NotNull] TEvent notification, CancellationToken cancellationToken = default)
where TEvent : class, IOpenIddictValidationEvent;
}
}

34
src/OpenIddict.Validation/Internal/OpenIddictValidationHandler.cs

@ -4,11 +4,8 @@
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using AspNet.Security.OAuth.Validation;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
@ -28,36 +25,5 @@ namespace OpenIddict.Validation
: base(options, logger, encoder, clock)
{
}
protected override async Task InitializeEventsAsync()
{
await base.InitializeEventsAsync();
// If an application provider instance or type was specified, import the application provider events.
if (Options.ApplicationEvents != null || Options.ApplicationEventsType != null)
{
// Resolve the user provider from the options or from the services container.
var events = Options.ApplicationEvents;
if (events == null)
{
events = Context.RequestServices.GetService(Options.ApplicationEventsType) as OAuthValidationEvents;
}
if (events == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The application events cannot be resolved from the dependency injection container. ")
.Append("Make sure they are correctly registered in 'ConfigureServices(IServiceCollection services)'.")
.ToString());
}
// Update the main events to invoke the user provider's event handlers.
Events.Import(events);
}
}
private new OpenIddictValidationEvents Events => (OpenIddictValidationEvents) base.Events;
private new OpenIddictValidationOptions Options => (OpenIddictValidationOptions) base.Options;
}
}

14
src/OpenIddict.Validation/Internal/OpenIddictValidationInitializer.cs

@ -6,7 +6,6 @@
using System;
using System.ComponentModel;
using AspNet.Security.OAuth.Validation;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.DataProtection;
@ -49,19 +48,6 @@ namespace OpenIddict.Validation
throw new ArgumentException("The options instance name cannot be null or empty.", nameof(name));
}
if (options.ApplicationEventsType != null)
{
if (options.ApplicationEvents != null)
{
throw new InvalidOperationException("Application events cannot be registered when a type is specified.");
}
if (!typeof(OAuthValidationEvents).IsAssignableFrom(options.ApplicationEventsType))
{
throw new InvalidOperationException("Application events must inherit from OAuthValidationEvents.");
}
}
if (options.DataProtectionProvider == null)
{
options.DataProtectionProvider = _dataProtectionProvider;

28
src/OpenIddict.Validation/Internal/OpenIddictValidationEvents.cs → src/OpenIddict.Validation/Internal/OpenIddictValidationProvider.cs

@ -19,8 +19,19 @@ namespace OpenIddict.Validation
/// Provides the logic necessary to extract, validate and handle OAuth2 requests.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictValidationEvents : OAuthValidationEvents
public class OpenIddictValidationProvider : OAuthValidationEvents
{
private readonly IOpenIddictValidationEventService _eventService;
public OpenIddictValidationProvider([NotNull] IOpenIddictValidationEventService eventService)
=> _eventService = eventService;
public override Task ApplyChallenge([NotNull] ApplyChallengeContext context)
=> _eventService.PublishAsync(new OpenIddictValidationEvents.ApplyChallenge(context));
public override Task CreateTicket([NotNull] CreateTicketContext context)
=> _eventService.PublishAsync(new OpenIddictValidationEvents.CreateTicket(context));
public override async Task DecryptToken([NotNull] DecryptTokenContext context)
{
var options = (OpenIddictValidationOptions) context.Options;
@ -80,16 +91,13 @@ namespace OpenIddict.Validation
context.Success();
}
await base.DecryptToken(context);
await _eventService.PublishAsync(new OpenIddictValidationEvents.DecryptToken(context));
}
public void Import([NotNull] OAuthValidationEvents events)
{
OnApplyChallenge = events.ApplyChallenge;
OnCreateTicket = events.CreateTicket;
OnDecryptToken = events.DecryptToken;
OnRetrieveToken = events.RetrieveToken;
OnValidateToken = events.ValidateToken;
}
public override Task RetrieveToken([NotNull] RetrieveTokenContext context)
=> _eventService.PublishAsync(new OpenIddictValidationEvents.RetrieveToken(context));
public override Task ValidateToken([NotNull] ValidateTokenContext context)
=> _eventService.PublishAsync(new OpenIddictValidationEvents.ValidateToken(context));
}
}

136
src/OpenIddict.Validation/OpenIddictValidationBuilder.cs

@ -7,10 +7,11 @@
using System;
using System.ComponentModel;
using System.Linq;
using AspNet.Security.OAuth.Validation;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.Abstractions;
using OpenIddict.Validation;
namespace Microsoft.Extensions.DependencyInjection
@ -41,93 +42,134 @@ namespace Microsoft.Extensions.DependencyInjection
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict validation configuration.
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder Configure([NotNull] Action<OpenIddictValidationOptions> configuration)
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent>(
[NotNull] IOpenIddictValidationEventHandler<TEvent> handler)
where TEvent : class, IOpenIddictValidationEvent
{
if (configuration == null)
if (handler == null)
{
throw new ArgumentNullException(nameof(configuration));
throw new ArgumentNullException(nameof(handler));
}
Services.Configure(OpenIddictValidationDefaults.AuthenticationScheme, configuration);
Services.AddSingleton(handler);
return this;
}
/// <summary>
/// Registers the specified values as valid audiences. Setting the audiences is recommended
/// when the authorization server issues access tokens for multiple distinct resource servers.
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="audiences">The audiences valid for this resource server.</param>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder AddAudiences([NotNull] params string[] audiences)
{
if (audiences == null)
{
throw new ArgumentNullException(nameof(audiences));
}
if (audiences.Any(audience => string.IsNullOrEmpty(audience)))
{
throw new ArgumentException("Audiences cannot be null or empty.", nameof(audiences));
}
return Configure(options => options.Audiences.UnionWith(audiences));
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, Task> handler)
where TEvent : class, IOpenIddictValidationEvent
=> AddEventHandler<TEvent>((notification, cancellationToken) => handler(notification));
/// <summary>
/// Registers application-specific OAuth2 validation events that are automatically
/// invoked for each request handled by the OpenIddict validation handler.
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="events">The custom <see cref="OAuthValidationEvents"/> service.</param>
/// <returns>The <see cref="OAuthValidationEvents"/>.</returns>
/// <param name="handler">The handler added to the DI container.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder RegisterEvents([NotNull] OAuthValidationEvents events)
public OpenIddictValidationBuilder AddEventHandler<TEvent>([NotNull] Func<TEvent, CancellationToken, Task> handler)
where TEvent : class, IOpenIddictValidationEvent
{
if (events == null)
if (handler == null)
{
throw new ArgumentNullException(nameof(events));
throw new ArgumentNullException(nameof(handler));
}
return Configure(options => options.ApplicationEvents = events);
return AddEventHandler(new OpenIddictValidationEventHandler<TEvent>(handler));
}
/// <summary>
/// Registers application-specific OAuth2 validation events that are automatically
/// invoked for each request handled by the OpenIddict validation handler.
/// Registers an event handler for the specified event type.
/// </summary>
/// <typeparam name="TEvents">The type of the custom <see cref="OAuthValidationEvents"/> service.</typeparam>
/// <typeparam name="TEvent">The type of the event.</typeparam>
/// <typeparam name="THandler">The type of the handler.</typeparam>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder RegisterEvents<TEvents>() where TEvents : OAuthValidationEvents
=> RegisterEvents(typeof(TEvents));
public OpenIddictValidationBuilder AddEventHandler<TEvent, THandler>(
ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictValidationEvent
where THandler : IOpenIddictValidationEventHandler<TEvent>
=> AddEventHandler<TEvent>(typeof(THandler));
/// <summary>
/// Registers application-specific OAuth2 validation events that are automatically
/// invoked for each request handled by the OpenIddict validation handler.
/// Registers an event handler for the specified event type.
/// </summary>
/// <param name="type">The type of the custom <see cref="OAuthValidationEvents"/> service.</param>
/// <param name="type">The type of the handler.</param>
/// <param name="lifetime">The lifetime of the registered service.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationBuilder RegisterEvents([NotNull] Type type)
public OpenIddictValidationBuilder AddEventHandler<TEvent>(
[NotNull] Type type, ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TEvent : class, IOpenIddictValidationEvent
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (!typeof(OAuthValidationEvents).IsAssignableFrom(type))
if (lifetime == ServiceLifetime.Transient)
{
throw new ArgumentException("Handlers cannot be registered as transient services.", nameof(lifetime));
}
if (!typeof(IOpenIddictValidationEventHandler<TEvent>).IsAssignableFrom(type))
{
throw new ArgumentException("The specified type is invalid.", nameof(type));
}
Services.TryAddScoped(type);
Services.Add(new ServiceDescriptor(typeof(IOpenIddictValidationEventHandler<TEvent>), type, lifetime));
return this;
}
/// <summary>
/// Amends the default OpenIddict validation configuration.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder Configure([NotNull] Action<OpenIddictValidationOptions> configuration)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
Services.Configure(OpenIddictValidationDefaults.AuthenticationScheme, configuration);
return Configure(options => options.ApplicationEventsType = type);
return this;
}
/// <summary>
/// Registers the specified values as valid audiences. Setting the audiences is recommended
/// when the authorization server issues access tokens for multiple distinct resource servers.
/// </summary>
/// <param name="audiences">The audiences valid for this resource server.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder AddAudiences([NotNull] params string[] audiences)
{
if (audiences == null)
{
throw new ArgumentNullException(nameof(audiences));
}
if (audiences.Any(audience => string.IsNullOrEmpty(audience)))
{
throw new ArgumentException("Audiences cannot be null or empty.", nameof(audiences));
}
return Configure(options => options.Audiences.UnionWith(audiences));
}
/// <summary>
@ -176,4 +218,4 @@ namespace Microsoft.Extensions.DependencyInjection
public OpenIddictValidationBuilder UseReferenceTokens()
=> Configure(options => options.UseReferenceTokens = true);
}
}
}

24
src/OpenIddict.Validation/OpenIddictValidationEvent.cs

@ -0,0 +1,24 @@
using System;
using JetBrains.Annotations;
namespace OpenIddict.Validation
{
/// <summary>
/// Represents an OpenIddict validation event.
/// </summary>
/// <typeparam name="TContext">The type of the context instance associated with the event.</typeparam>
public class OpenIddictValidationEvent<TContext> : IOpenIddictValidationEvent where TContext : class
{
/// <summary>
/// Creates a new instance of <see cref="OpenIddictValidationEvent{TContext}"/>.
/// </summary>
/// <param name="context">The context instance associated with the event.</param>
public OpenIddictValidationEvent([NotNull] TContext context)
=> Context = context ?? throw new ArgumentNullException(nameof(context));
/// <summary>
/// Gets the context instance associated with the event.
/// </summary>
public TContext Context { get; }
}
}

49
src/OpenIddict.Validation/OpenIddictValidationEventHandler.cs

@ -0,0 +1,49 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace OpenIddict.Validation
{
/// <summary>
/// Represents a handler able to process <typeparamref name="TEvent"/> events.
/// </summary>
/// <typeparam name="TEvent">The type of the events handled by this instance.</typeparam>
public class OpenIddictValidationEventHandler<TEvent> : IOpenIddictValidationEventHandler<TEvent>
where TEvent : class, IOpenIddictValidationEvent
{
private readonly Func<TEvent, CancellationToken, Task> _handler;
/// <summary>
/// Creates a new event using the specified handler delegate.
/// </summary>
/// <param name="handler">The event handler delegate</param>
public OpenIddictValidationEventHandler([NotNull] Func<TEvent, CancellationToken, Task> handler)
=> _handler = handler ?? throw new ArgumentNullException(nameof(handler));
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="notification">The event to process.</param>
/// <param name="cancellationToken">
/// The <see cref="CancellationToken"/> that can be used to abort the operation.
/// </param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
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);
}
}
}

63
src/OpenIddict.Validation/OpenIddictValidationEventService.cs

@ -0,0 +1,63 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace OpenIddict.Validation
{
/// <summary>
/// Dispatches notifications by invoking the corresponding handlers.
/// </summary>
public class OpenIddictValidationEventService : IOpenIddictValidationEventService
{
private readonly IServiceProvider _provider;
public OpenIddictValidationEventService([NotNull] IServiceProvider provider)
{
_provider = provider;
}
/// <summary>
/// Publishes a new event.
/// </summary>
/// <typeparam name="TEvent">The type of the event to publish.</typeparam>
/// <param name="notification">The event to publish.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
public async Task PublishAsync<TEvent>([NotNull] TEvent notification, CancellationToken cancellationToken = default)
where TEvent : class, IOpenIddictValidationEvent
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
foreach (var handler in _provider.GetServices<IOpenIddictValidationEventHandler<TEvent>>())
{
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;
}
}
}
}
}

71
src/OpenIddict.Validation/OpenIddictValidationEvents.cs

@ -0,0 +1,71 @@
using AspNet.Security.OAuth.Validation;
using JetBrains.Annotations;
namespace OpenIddict.Validation
{
/// <summary>
/// Contains common events used by the OpenIddict validation handler.
/// </summary>
public static class OpenIddictValidationEvents
{
/// <summary>
/// Invoked when a challenge response is returned to the caller.
/// </summary>
public sealed class ApplyChallenge : OpenIddictValidationEvent<ApplyChallengeContext>
{
/// <summary>
/// Creates a new instance of <see cref="ApplyChallenge"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ApplyChallenge([NotNull] ApplyChallengeContext context) : base(context) { }
}
/// <summary>
/// Invoked when a ticket is to be created from an introspection response.
/// </summary>
public sealed class CreateTicket : OpenIddictValidationEvent<CreateTicketContext>
{
/// <summary>
/// Creates a new instance of <see cref="CreateTicket"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public CreateTicket([NotNull] CreateTicketContext context) : base(context) { }
}
/// <summary>
/// Invoked when a token is to be decrypted.
/// </summary>
public sealed class DecryptToken : OpenIddictValidationEvent<DecryptTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="DecryptToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public DecryptToken([NotNull] DecryptTokenContext context) : base(context) { }
}
/// <summary>
/// Invoked when a token is to be parsed from a newly-received request.
/// </summary>
public sealed class RetrieveToken : OpenIddictValidationEvent<RetrieveTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="RetrieveToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public RetrieveToken([NotNull] RetrieveTokenContext context) : base(context) { }
}
/// <summary>
/// Invoked when a token is to be validated, before final processing.
/// </summary>
public sealed class ValidateToken : OpenIddictValidationEvent<ValidateTokenContext>
{
/// <summary>
/// Creates a new instance of <see cref="ValidateToken"/>.
/// </summary>
/// <param name="context">The context instance associated with the notification.</param>
public ValidateToken([NotNull] ValidateTokenContext context) : base(context) { }
}
}
}

3
src/OpenIddict.Validation/OpenIddictValidationExtensions.cs

@ -34,8 +34,9 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Services.AddAuthentication();
builder.Services.TryAddScoped<OpenIddictValidationEvents>();
builder.Services.TryAddScoped<IOpenIddictValidationEventService, OpenIddictValidationEventService>();
builder.Services.TryAddScoped<OpenIddictValidationHandler>();
builder.Services.TryAddScoped<OpenIddictValidationProvider>();
// Note: TryAddEnumerable() is used here to ensure the initializer is only registered once.
builder.Services.TryAddEnumerable(new[]

17
src/OpenIddict.Validation/OpenIddictValidationOptions.cs

@ -4,7 +4,6 @@
* the license and the contributors participating to this project.
*/
using System;
using AspNet.Security.OAuth.Validation;
namespace OpenIddict.Validation
@ -20,23 +19,9 @@ namespace OpenIddict.Validation
public OpenIddictValidationOptions()
{
Events = null;
EventsType = typeof(OpenIddictValidationEvents);
EventsType = typeof(OpenIddictValidationProvider);
}
/// <summary>
/// Gets or sets the user-provided <see cref="OAuthValidationEvents"/> that the OpenIddict
/// validation handler invokes to enable developer control over the entire authentication process.
/// </summary>
public OAuthValidationEvents ApplicationEvents { get; set; }
/// <summary>
/// Gets or sets the user-provided provider type that the OpenIddict validation handler
/// instantiates to enable developer control over the entire authentication process. When this
/// property is set, the provider is resolved from the services container. If the provider is not
/// guaranteed to be thread-safe, registering it as a scoped dependency is strongly recommended.
/// </summary>
public Type ApplicationEventsType { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether reference tokens are used.
/// </summary>

47
test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs

@ -9,7 +9,6 @@ using System.Text;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Client;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -44,52 +43,6 @@ namespace OpenIddict.Server.Tests
Assert.Equal("A random number generator must be registered.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenApplicationProviderTypeAndInstanceAreProvided()
{
// Arrange
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options =>
{
options.ApplicationProvider = new OpenIdConnectServerProvider();
options.ApplicationProviderType = typeof(OpenIdConnectServerProvider);
});
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/");
});
// Assert
Assert.Equal("An application provider cannot be registered when a type is specified.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionForInvalidApplicationProviderType()
{
// Arrange
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.ApplicationProviderType = typeof(object));
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/");
});
// Assert
Assert.Equal("Application providers must inherit from OpenIdConnectServerProvider.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenNoFlowIsEnabled()
{

125
test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs

@ -7,8 +7,9 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@ -16,11 +17,63 @@ using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Moq;
using Xunit;
using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server.Tests
{
public class OpenIddictServerBuilderTests
{
[Fact]
public void AddEventHandler_HandlerIsAttached()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
var handler = new OpenIddictServerEventHandler<ApplyAuthorizationResponse>(
(notification, cancellationToken) => Task.CompletedTask);
// Act
builder.AddEventHandler(handler);
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler<ApplyAuthorizationResponse>) &&
service.ImplementationInstance == handler);
}
[Fact]
public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler<ApplyAuthorizationResponse>(typeof(object));
});
Assert.Equal("type", exception.ParamName);
Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
public void AddEventHandler_HandlerIsRegistered()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.AddEventHandler<ApplyAuthorizationResponse, CustomHandler>();
// Assert
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictServerEventHandler<ApplyAuthorizationResponse>) &&
service.ImplementationType == typeof(CustomHandler));
}
[Fact]
public void Configure_OptionsAreCorrectlyAmended()
{
@ -600,69 +653,6 @@ namespace OpenIddict.Server.Tests
Assert.Equal(new Uri("http://www.fabrikam.com/"), options.Issuer);
}
[Fact]
public void RegisterProvider_ProviderIsAttached()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.RegisterProvider(new OpenIdConnectServerProvider());
var options = GetOptions(services);
// Assert
Assert.NotNull(options.ApplicationProvider);
}
[Fact]
public void RegisterProvider_ThrowsAnExceptionForInvalidProviderType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.RegisterProvider(typeof(object));
});
Assert.Equal("type", exception.ParamName);
Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
public void RegisterProvider_ProviderTypeIsAttached()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.RegisterProvider(typeof(OpenIdConnectServerProvider));
var options = GetOptions(services);
// Assert
Assert.Equal(typeof(OpenIdConnectServerProvider), options.ApplicationProviderType);
}
[Fact]
public void RegisterProvider_ProviderIsRegistered()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.RegisterProvider(typeof(OpenIdConnectServerProvider));
// Assert
Assert.Contains(services, service => service.ServiceType == typeof(OpenIdConnectServerProvider));
}
[Fact]
public void RegisterClaims_ClaimsAreAdded()
{
@ -757,5 +747,12 @@ namespace OpenIddict.Server.Tests
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictServerOptions>>();
return options.Get(OpenIddictServerDefaults.AuthenticationScheme);
}
public class CustomHandler : OpenIddictServerEventHandler<ApplyAuthorizationResponse>
{
public CustomHandler(Func<ApplyAuthorizationResponse, CancellationToken, Task> handler) : base(handler)
{
}
}
}
}

110
test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationInitializerTests.cs

@ -1,110 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Threading.Tasks;
using AspNet.Security.OAuth.Validation;
using AspNet.Security.OpenIdConnect.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;
namespace OpenIddict.Validation.Tests
{
public class OpenIddictValidationInitializerTests
{
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenApplicationEventsTypeAndInstanceAreProvided()
{
// Arrange
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options =>
{
options.ApplicationEvents = new OAuthValidationEvents();
options.ApplicationEventsType = typeof(OAuthValidationEvents);
});
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/");
});
// Assert
Assert.Equal("Application events cannot be registered when a type is specified.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionForInvalidApplicationEventsType()
{
// Arrange
var server = CreateAuthorizationServer(builder =>
{
builder.Configure(options => options.ApplicationEventsType = typeof(object));
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/");
});
// Assert
Assert.Equal("Application events must inherit from OAuthValidationEvents.", exception.Message);
}
private static TestServer CreateAuthorizationServer(Action<OpenIddictValidationBuilder> configuration = null)
{
var builder = new WebHostBuilder();
builder.UseEnvironment("Testing");
builder.ConfigureLogging(options => options.AddDebug());
builder.ConfigureServices(services =>
{
services.AddAuthentication();
services.AddOptions();
services.AddDistributedMemoryCache();
services.AddOpenIddict()
.AddCore(options =>
{
options.SetDefaultApplicationEntity<OpenIddictApplication>()
.SetDefaultAuthorizationEntity<OpenIddictAuthorization>()
.SetDefaultScopeEntity<OpenIddictScope>()
.SetDefaultTokenEntity<OpenIddictToken>();
})
.AddValidation(options => configuration?.Invoke(options));
});
builder.Configure(app =>
{
app.UseAuthentication();
app.Run(context => context.ChallengeAsync(OpenIddictValidationDefaults.AuthenticationScheme));
});
return new TestServer(builder);
}
public class OpenIddictApplication { }
public class OpenIddictAuthorization { }
public class OpenIddictScope { }
public class OpenIddictToken { }
}
}

2
test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationEventsTests.cs → test/OpenIddict.Validation.Tests/Internal/OpenIddictValidationProviderTests.cs

@ -34,7 +34,7 @@ using Xunit;
namespace OpenIddict.Validation.Tests
{
public class OpenIddictValidationEventsTests
public class OpenIddictValidationProviderTests
{
[Fact]
public async Task DecryptToken_ThrowsAnExceptionWhenTokenManagerIsNotRegistered()

79
test/OpenIddict.Validation.Tests/OpenIddictValidationBuilderTests.cs

@ -5,109 +5,99 @@
*/
using System;
using AspNet.Security.OAuth.Validation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
using static OpenIddict.Validation.OpenIddictValidationEvents;
namespace OpenIddict.Validation.Tests
{
public class OpenIddictValidationBuilderTests
{
[Fact]
public void Configure_OptionsAreCorrectlyAmended()
public void AddEventHandler_HandlerIsAttached()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
var handler = new OpenIddictValidationEventHandler<CreateTicket>(
(notification, cancellationToken) => Task.CompletedTask);
// Act
builder.Configure(configuration => configuration.ClaimsIssuer = "custom_issuer");
var options = GetOptions(services);
builder.AddEventHandler(handler);
// Assert
Assert.Equal("custom_issuer", options.ClaimsIssuer);
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler<CreateTicket>) &&
service.ImplementationInstance == handler);
}
[Fact]
public void AddAudiences_AudiencesAreAdded()
public void AddEventHandler_ThrowsAnExceptionForInvalidHandlerType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.AddAudiences("Fabrikam", "Contoso");
var options = GetOptions(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.AddEventHandler<CreateTicket>(typeof(object));
});
// Assert
Assert.Equal(new[] { "Fabrikam", "Contoso" }, options.Audiences);
Assert.Equal("type", exception.ParamName);
Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
public void RegisterEvents_EventsAreAttached()
public void AddEventHandler_HandlerIsRegistered()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.RegisterEvents(new OAuthValidationEvents());
var options = GetOptions(services);
builder.AddEventHandler<CreateTicket, CustomHandler>();
// Assert
Assert.NotNull(options.ApplicationEvents);
Assert.Contains(services, service =>
service.ServiceType == typeof(IOpenIddictValidationEventHandler<CreateTicket>) &&
service.ImplementationType == typeof(CustomHandler));
}
[Fact]
public void RegisterEvents_ThrowsAnExceptionForInvalidEventsType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return builder.RegisterEvents(typeof(object));
});
Assert.Equal("type", exception.ParamName);
Assert.StartsWith("The specified type is invalid.", exception.Message);
}
[Fact]
public void RegisterEvents_EventsTypeIsAttached()
public void Configure_OptionsAreCorrectlyAmended()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.RegisterEvents(typeof(OAuthValidationEvents));
builder.Configure(configuration => configuration.ClaimsIssuer = "custom_issuer");
var options = GetOptions(services);
// Assert
Assert.Equal(typeof(OAuthValidationEvents), options.ApplicationEventsType);
Assert.Equal("custom_issuer", options.ClaimsIssuer);
}
[Fact]
public void RegisterEvents_EventsAreRegistered()
public void AddAudiences_AudiencesAreAdded()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.RegisterEvents(typeof(OAuthValidationEvents));
builder.AddAudiences("Fabrikam", "Contoso");
var options = GetOptions(services);
// Assert
Assert.Contains(services, service => service.ServiceType == typeof(OAuthValidationEvents));
Assert.Equal(new[] { "Fabrikam", "Contoso" }, options.Audiences);
}
[Fact]
@ -186,5 +176,12 @@ namespace OpenIddict.Validation.Tests
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictValidationOptions>>();
return options.Get(OpenIddictValidationDefaults.AuthenticationScheme);
}
public class CustomHandler : OpenIddictValidationEventHandler<CreateTicket>
{
public CustomHandler(Func<CreateTicket, CancellationToken, Task> handler) : base(handler)
{
}
}
}
}

Loading…
Cancel
Save