From f54ba7f6dd2d94cc4df4a06308535b2dd6949cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Tue, 8 Sep 2020 17:21:17 +0200 Subject: [PATCH] Allow built-in scope validation to be partially executed when the degraded mode is enabled --- .../Resources/OpenIddictResources.resx | 4 +++ ...OpenIddictServerHandlers.Authentication.cs | 16 +++++++--- .../OpenIddictServerHandlers.Device.cs | 16 +++++++--- .../OpenIddictServerHandlers.Exchange.cs | 16 +++++++--- ...ctServerIntegrationTests.Authentication.cs | 32 +------------------ ...enIddictServerIntegrationTests.Exchange.cs | 4 +++ 6 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx b/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx index c4fbcd42..f80328ab 100644 --- a/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx @@ -1776,6 +1776,10 @@ To register the OpenIddict core services, reference the 'OpenIddict.Core' packag The token shouldn't be null or empty at this point. {Locked} + + The OpenIddict Core services should be registered. + {Locked} + An error occurred while validating the token '{Token}'. {Locked} diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs index 8db9b064..e01c61c5 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs @@ -1080,13 +1080,15 @@ namespace OpenIddict.Server /// /// Contains the logic responsible of rejecting authorization requests that use unregistered scopes. - /// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled. + /// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled. /// public class ValidateScopes : IOpenIddictServerHandler { - private readonly IOpenIddictScopeManager _scopeManager; + private readonly IOpenIddictScopeManager? _scopeManager; - public ValidateScopes() => throw new InvalidOperationException(SR.GetResourceString(SR.ID1015)); + public ValidateScopes() + { + } public ValidateScopes(IOpenIddictScopeManager scopeManager) => _scopeManager = scopeManager; @@ -1097,7 +1099,6 @@ namespace OpenIddict.Server public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() - .AddFilter() .UseScopedHandler() .SetOrder(ValidateClientRedirectUri.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) @@ -1115,8 +1116,13 @@ namespace OpenIddict.Server var scopes = new HashSet(context.Request.GetScopes(), StringComparer.Ordinal); scopes.ExceptWith(context.Options.Scopes); - if (scopes.Count != 0) + // Note: the remaining scopes are only checked if the degraded mode was not enabled, + // as this requires using the scope manager, which is never used with the degraded mode, + // even if the service was registered and resolved from the dependency injection container. + if (scopes.Count != 0 && !context.Options.EnableDegradedMode) { + Debug.Assert(_scopeManager != null, SR.GetResourceString(SR.ID5011)); + await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { var name = await _scopeManager.GetNameAsync(scope); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs index b767d343..9904622c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs @@ -364,13 +364,15 @@ namespace OpenIddict.Server /// /// Contains the logic responsible of rejecting authorization requests that use unregistered scopes. - /// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled. + /// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled. /// public class ValidateScopes : IOpenIddictServerHandler { - private readonly IOpenIddictScopeManager _scopeManager; + private readonly IOpenIddictScopeManager? _scopeManager; - public ValidateScopes() => throw new InvalidOperationException(SR.GetResourceString(SR.ID1015)); + public ValidateScopes() + { + } public ValidateScopes(IOpenIddictScopeManager scopeManager) => _scopeManager = scopeManager; @@ -381,7 +383,6 @@ namespace OpenIddict.Server public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() - .AddFilter() .UseScopedHandler() .SetOrder(ValidateClientIdParameter.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) @@ -399,8 +400,13 @@ namespace OpenIddict.Server var scopes = new HashSet(context.Request.GetScopes(), StringComparer.Ordinal); scopes.ExceptWith(context.Options.Scopes); - if (scopes.Count != 0) + // Note: the remaining scopes are only checked if the degraded mode was not enabled, + // as this requires using the scope manager, which is never used with the degraded mode, + // even if the service was registered and resolved from the dependency injection container. + if (scopes.Count != 0 && !context.Options.EnableDegradedMode) { + Debug.Assert(_scopeManager != null, SR.GetResourceString(SR.ID5011)); + await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { var name = await _scopeManager.GetNameAsync(scope); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs index ebfea6eb..10777c2e 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs @@ -645,13 +645,15 @@ namespace OpenIddict.Server /// /// Contains the logic responsible of rejecting authorization requests that use unregistered scopes. - /// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled. + /// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled. /// public class ValidateScopes : IOpenIddictServerHandler { - private readonly IOpenIddictScopeManager _scopeManager; + private readonly IOpenIddictScopeManager? _scopeManager; - public ValidateScopes() => throw new InvalidOperationException(SR.GetResourceString(SR.ID1015)); + public ValidateScopes() + { + } public ValidateScopes(IOpenIddictScopeManager scopeManager) => _scopeManager = scopeManager; @@ -662,7 +664,6 @@ namespace OpenIddict.Server public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() - .AddFilter() .UseScopedHandler() .SetOrder(ValidatePasswordParameters.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) @@ -680,8 +681,13 @@ namespace OpenIddict.Server var scopes = new HashSet(context.Request.GetScopes(), StringComparer.Ordinal); scopes.ExceptWith(context.Options.Scopes); - if (scopes.Count != 0) + // Note: the remaining scopes are only checked if the degraded mode was not enabled, + // as this requires using the scope manager, which is never used with the degraded mode, + // even if the service was registered and resolved from the dependency injection container. + if (scopes.Count != 0 && !context.Options.EnableDegradedMode) { + Debug.Assert(_scopeManager != null, SR.GetResourceString(SR.ID5011)); + await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray())) { var name = await _scopeManager.GetNameAsync(scope); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs index 7f655ee1..5b4d3c0b 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs @@ -615,38 +615,8 @@ namespace OpenIddict.Server.IntegrationTests // Arrange await using var server = await CreateServerAsync(options => { + options.EnableDegradedMode(); options.RegisterScopes("registered_scope"); - options.SetRevocationEndpointUris(Array.Empty()); - options.DisableTokenStorage(); - options.DisableSlidingRefreshTokenExpiration(); - - options.Services.AddSingleton(CreateApplicationManager(mock => - { - var application = new OpenIddictApplication(); - - mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) - .ReturnsAsync(application); - - mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny())) - .ReturnsAsync(true); - - mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) - .ReturnsAsync(true); - })); - - options.Services.AddSingleton(CreateApplicationManager(mock => - { - var application = new OpenIddictApplication(); - - mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) - .ReturnsAsync(application); - - mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny())) - .ReturnsAsync(true); - - mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny())) - .ReturnsAsync(true); - })); options.AddEventHandler(builder => builder.UseInlineHandler(context => diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs index 0d5821b8..d5905f8e 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs @@ -886,6 +886,7 @@ namespace OpenIddict.Server.IntegrationTests await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); + options.RegisterScopes(Scopes.Phone, Scopes.Profile); options.AddEventHandler(builder => { @@ -930,6 +931,7 @@ namespace OpenIddict.Server.IntegrationTests await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); + options.RegisterScopes(Scopes.Phone, Scopes.Profile); options.AddEventHandler(builder => { @@ -974,6 +976,7 @@ namespace OpenIddict.Server.IntegrationTests await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); + options.RegisterScopes(Scopes.Phone, Scopes.Profile); options.AddEventHandler(builder => { @@ -1016,6 +1019,7 @@ namespace OpenIddict.Server.IntegrationTests await using var server = await CreateServerAsync(options => { options.EnableDegradedMode(); + options.RegisterScopes(Scopes.Phone, Scopes.Profile); options.AddEventHandler(builder => {