From f0acd0cd643579afa18e3d4b831bffcdb2059ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Wed, 14 Dec 2022 13:43:44 +0100 Subject: [PATCH 01/10] Update Versions.props to build 4.0.0-rc2 packages --- eng/Versions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 6d4ec769..3aff94a2 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -5,9 +5,9 @@ 0 0 $(MajorVersion).$(MinorVersion).$(PatchVersion) - rc1 + rc2 - Release Candidate 1 + Release Candidate 2 false release true From 604889b12997c27f87bc956bba419aaeb8ac35ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 12 Dec 2022 12:05:06 +0100 Subject: [PATCH 02/10] Update ProcessChallengeContext/ProcessSignOutContext.Nonce's documentation to emphasize that nonces should be generated using a crypto-secure RNG --- src/OpenIddict.Client/OpenIddictClientEvents.cs | 4 ++++ src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/OpenIddict.Client/OpenIddictClientEvents.cs b/src/OpenIddict.Client/OpenIddictClientEvents.cs index a98cd386..fbe07070 100644 --- a/src/OpenIddict.Client/OpenIddictClientEvents.cs +++ b/src/OpenIddict.Client/OpenIddictClientEvents.cs @@ -792,6 +792,8 @@ public static partial class OpenIddictClientEvents /// /// Gets or sets the nonce that will be used for the challenge demand, if applicable. + /// Note: this value MUST NOT be user-defined or extracted from any request and MUST be random + /// (generated by a random number generator suitable for cryptographic operations). /// public string? Nonce { get; set; } @@ -931,6 +933,8 @@ public static partial class OpenIddictClientEvents /// /// Gets or sets the nonce that will be used for the sign-out demand, if applicable. + /// Note: this value MUST NOT be user-defined or extracted from any request and MUST be random + /// (generated by a random number generator suitable for cryptographic operations). /// public string? Nonce { get; set; } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index 3b32fe62..c3503c8c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -1339,7 +1339,7 @@ public static partial class OpenIddictServerHandlers do { // Note: unlike other reference tokens, user codes are meant to be used by humans, - // who may have to enter it in a web form. To ensure it remains easy enough to type + // who may have to enter it in a web form. To ensure they remain easy enough to type // even by users with non-Latin keyboards, user codes generated by OpenIddict are // only compound of 12 digits, generated using a crypto-secure random number generator. // In this case, the resulting user code is estimated to have at most ~40 bits of entropy. From bfa95d094a684be7bcc5b1869238c5eae3b28cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 12 Dec 2022 22:06:34 +0100 Subject: [PATCH 03/10] Unify the token entry properties restoration logic and fix an issue affecting device codes and ASP.NET Core Data Protection --- .../OpenIddictResources.resx | 19 +- ...ClientDataProtectionHandlers.Protection.cs | 10 +- .../OpenIddictClientBuilder.cs | 3 +- .../OpenIddictClientEvents.Protection.cs | 16 ++ .../OpenIddictClientExtensions.cs | 2 + .../OpenIddictClientHandlerFilters.cs | 32 +++ .../OpenIddictClientHandlers.Protection.cs | 111 +++++----- .../OpenIddictClientHandlers.cs | 3 + .../OpenIddictClientOptions.cs | 3 +- ...ServerDataProtectionHandlers.Protection.cs | 26 +-- .../OpenIddictServerEvents.Protection.cs | 16 ++ .../OpenIddictServerExtensions.cs | 2 + .../OpenIddictServerHandlerFilters.cs | 32 +++ .../OpenIddictServerHandlers.Protection.cs | 203 +++++++++--------- .../OpenIddictServerHandlers.cs | 23 +- ...dationDataProtectionHandlers.Protection.cs | 6 +- .../OpenIddictValidationEvents.Protection.cs | 10 + .../OpenIddictValidationExtensions.cs | 2 + .../OpenIddictValidationHandlerFilters.cs | 32 +++ ...OpenIddictValidationHandlers.Protection.cs | 84 ++++---- ...enIddictServerIntegrationTests.Exchange.cs | 48 ++++- .../OpenIddictServerIntegrationTests.cs | 9 + 22 files changed, 468 insertions(+), 224 deletions(-) diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 13050f5d..a4a1d0e6 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1936,6 +1936,12 @@ Consider registering a certificate using 'services.AddOpenIddict().AddClient().A The number of written bytes ({0}) doesn't match the expected value ({1}). + + The token identifier shouldn't be null or empty at this point. + + + The authorization identifier shouldn't be null or empty at this point. + An error occurred while validating the token '{Token}'. @@ -1976,14 +1982,21 @@ Consider registering a certificate using 'services.AddOpenIddict().AddClient().A The token entry for '{Type}' token '{Identifier}' was successfully created. - A new '{Type}' token was successfully created: {Payload}. + A new '{Type}' JSON Web Token was successfully created: {Payload}. The principal used to create the token contained the following claims: {Claims}. - The token entry for '{Type}' token '{Identifier}' was successfully converted to a reference token with the identifier '{ReferenceId}'. + The token payload ({Payload}) was successfully attached to the token entry '{Identifier}' of type '{Type}'. + + + The reference identifier ({ReferenceId}) was successfully attached to the token entry '{Identifier}' of type '{Type}'. + + + A new '{Type}' ASP.NET Core Data Protection token was successfully created: {Payload}. +The principal used to create the token contained the following claims: {Claims}. - The reference token entry for device code '{Identifier}' was successfully updated'. + The token entry for device code '{Identifier}' was successfully updated with the new payload. The authorization request was successfully extracted: {Request}. diff --git a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlers.Protection.cs b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlers.Protection.cs index 629661a6..9bb5b160 100644 --- a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlers.Protection.cs +++ b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlers.Protection.cs @@ -104,11 +104,11 @@ public static partial class OpenIddictClientDataProtectionHandlers // // Note: reference tokens are encrypted using a different "purpose" string than non-reference tokens. var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector( - (type, context.TokenId) switch + (type, context.IsReferenceToken) switch { - (TokenTypeHints.StateToken, { Length: not 0 }) + (TokenTypeHints.StateToken, true) => new[] { Handlers.Client, Formats.StateToken, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.StateToken, null or { Length: 0 }) + (TokenTypeHints.StateToken, false) => new[] { Handlers.Client, Formats.StateToken, Schemes.Server }, _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0003)) @@ -220,7 +220,7 @@ public static partial class OpenIddictClientDataProtectionHandlers // // Note: reference tokens are encrypted using a different "purpose" string than non-reference tokens. var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector( - (context.TokenType, context.PersistTokenPayload) switch + (context.TokenType, context.IsReferenceToken) switch { (TokenTypeHints.StateToken, true) => new[] { Handlers.Client, Formats.StateToken, Features.ReferenceTokens, Schemes.Server }, @@ -237,7 +237,7 @@ public static partial class OpenIddictClientDataProtectionHandlers context.Token = Base64UrlEncoder.Encode(protector.Protect(buffer.ToArray())); - context.Logger.LogTrace(SR.GetResourceString(SR.ID6013), context.TokenType, + context.Logger.LogTrace(SR.GetResourceString(SR.ID6016), context.TokenType, context.Token, context.Principal.Claims); return default; diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index bd7e4574..df2a9fb8 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -881,8 +881,7 @@ public sealed class OpenIddictClientBuilder /// /// Disables token storage, so that no database entry is created /// for the tokens and codes returned by the OpenIddict client. - /// Using this option is generally NOT recommended as it prevents - /// the tokens from being revoked (if needed). + /// Using this option is generally NOT recommended. /// /// The instance. public OpenIddictClientBuilder DisableTokenStorage() diff --git a/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs b/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs index b820d29c..9754af17 100644 --- a/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs @@ -40,6 +40,12 @@ public static partial class OpenIddictClientEvents /// public bool CreateTokenEntry { get; set; } + /// + /// Gets or sets a boolean indicating whether a reference token should be used + /// and, if applicable, returned to the caller instead of the actual token payload. + /// + public bool IsReferenceToken { get; set; } + /// /// Gets or sets a boolean indicating whether the token payload /// should be persisted alongside the token metadata in the database. @@ -129,6 +135,16 @@ public static partial class OpenIddictClientEvents /// public string? TokenTypeHint { get; set; } = default!; + /// + /// Gets or sets a boolean indicating whether the validated token is a reference token. + /// + public bool IsReferenceToken { get; set; } + + /// + /// Gets or sets the authorization entry identifier associated with the token, if applicable. + /// + public string? AuthorizationId { get; set; } + /// /// Gets or sets the token entry identifier associated with the token, if applicable. /// diff --git a/src/OpenIddict.Client/OpenIddictClientExtensions.cs b/src/OpenIddict.Client/OpenIddictClientExtensions.cs index d34db533..348e4e08 100644 --- a/src/OpenIddict.Client/OpenIddictClientExtensions.cs +++ b/src/OpenIddict.Client/OpenIddictClientExtensions.cs @@ -37,6 +37,7 @@ public static class OpenIddictClientExtensions // Register the built-in filters used by the default OpenIddict client event handlers. builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); @@ -54,6 +55,7 @@ public static class OpenIddictClientExtensions builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); diff --git a/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs b/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs index b627d36b..0a1825f6 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs @@ -27,6 +27,22 @@ public static class OpenIddictClientHandlerFilters } } + /// + /// Represents a filter that excludes the associated handlers if no authorization identifier is resolved from the token. + /// + public sealed class RequireAuthorizationIdResolved : IOpenIddictClientHandlerFilter + { + public ValueTask IsActiveAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(!string.IsNullOrEmpty(context.AuthorizationId)); + } + } + /// /// Represents a filter that excludes the associated handlers if no backchannel access token is validated. /// @@ -305,6 +321,22 @@ public static class OpenIddictClientHandlerFilters } } + /// + /// Represents a filter that excludes the associated handlers if no token identifier is resolved from the token. + /// + public sealed class RequireTokenIdResolved : IOpenIddictClientHandlerFilter + { + public ValueTask IsActiveAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(!string.IsNullOrEmpty(context.TokenId)); + } + } + /// /// Represents a filter that excludes the associated handlers if the token payload is not persisted in the database. /// diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs index d75924a9..35d9ef66 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs @@ -28,7 +28,7 @@ public static partial class OpenIddictClientHandlers ValidateReferenceTokenIdentifier.Descriptor, ValidateIdentityModelToken.Descriptor, MapInternalClaims.Descriptor, - RestoreReferenceTokenProperties.Descriptor, + RestoreTokenEntryProperties.Descriptor, ValidatePrincipal.Descriptor, ValidateExpirationDate.Descriptor, ValidateTokenEntry.Descriptor, @@ -39,7 +39,7 @@ public static partial class OpenIddictClientHandlers AttachSecurityCredentials.Descriptor, CreateTokenEntry.Descriptor, GenerateIdentityModelToken.Descriptor, - ConvertReferenceToken.Descriptor); + AttachTokenPayload.Descriptor); /// /// Contains the logic responsible for resolving the validation parameters used to validate tokens. @@ -225,6 +225,7 @@ public static partial class OpenIddictClientHandlers // Replace the token parameter by the payload resolved from the token entry // and store the identifier of the reference token so it can be later // used to restore the properties associated with the token. + context.IsReferenceToken = true; context.Token = payload; context.TokenId = await _tokenManager.GetIdAsync(token); } @@ -412,16 +413,16 @@ public static partial class OpenIddictClientHandlers } /// - /// Contains the logic responsible for restoring the properties associated with a reference token entry. + /// Contains the logic responsible for restoring the properties associated with a token entry. /// Note: this handler is not used when token storage is disabled. /// - public sealed class RestoreReferenceTokenProperties : IOpenIddictClientHandler + public sealed class RestoreTokenEntryProperties : IOpenIddictClientHandler { private readonly IOpenIddictTokenManager _tokenManager; - public RestoreReferenceTokenProperties() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0318)); + public RestoreTokenEntryProperties() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0318)); - public RestoreReferenceTokenProperties(IOpenIddictTokenManager tokenManager) + public RestoreTokenEntryProperties(IOpenIddictTokenManager tokenManager) => _tokenManager = tokenManager ?? throw new ArgumentNullException(nameof(tokenManager)); /// @@ -430,7 +431,7 @@ public static partial class OpenIddictClientHandlers public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() - .UseScopedHandler() + .UseScopedHandler() .SetOrder(MapInternalClaims.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -442,20 +443,40 @@ public static partial class OpenIddictClientHandlers throw new ArgumentNullException(nameof(context)); } - if (context.Principal is null || string.IsNullOrEmpty(context.TokenId)) + if (context.Principal is null) { return; } - var token = await _tokenManager.FindByIdAsync(context.TokenId) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0021)); + // Extract the token identifier from the authentication principal. + // + // If no token identifier can be found, this indicates that the token + // has no backing database entry (e.g if token storage was disabled). + var identifier = context.Principal.GetTokenId(); + if (string.IsNullOrEmpty(identifier)) + { + return; + } + + // If the token entry cannot be found, return a generic error. + var token = await _tokenManager.FindByIdAsync(identifier); + if (token is null) + { + context.Reject( + error: Errors.InvalidToken, + description: SR.GetResourceString(SR.ID2019), + uri: SR.FormatID8000(SR.ID2019)); + + return; + } // Restore the creation/expiration dates/identifiers from the token entry metadata. - context.Principal.SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) - .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token)) - .SetTokenId(await _tokenManager.GetIdAsync(token)) - .SetTokenType(await _tokenManager.GetTypeAsync(token)); + context.Principal + .SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) + .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) + .SetAuthorizationId(context.AuthorizationId = await _tokenManager.GetAuthorizationIdAsync(token)) + .SetTokenId(context.TokenId = await _tokenManager.GetIdAsync(token)) + .SetTokenType(await _tokenManager.GetTypeAsync(token)); } } @@ -470,7 +491,7 @@ public static partial class OpenIddictClientHandlers public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() .UseSingletonHandler() - .SetOrder(RestoreReferenceTokenProperties.Descriptor.Order + 1_000) + .SetOrder(RestoreTokenEntryProperties.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -571,6 +592,7 @@ public static partial class OpenIddictClientHandlers public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() + .AddFilter() .UseScopedHandler() .SetOrder(ValidateExpirationDate.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) @@ -585,28 +607,14 @@ public static partial class OpenIddictClientHandlers } Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); + Debug.Assert(!string.IsNullOrEmpty(context.TokenId), SR.GetResourceString(SR.ID4017)); - var identifier = context.Principal.GetTokenId(); - if (string.IsNullOrEmpty(identifier)) - { - return; - } - - // If the token entry cannot be found, return a generic error. - var token = await _tokenManager.FindByIdAsync(identifier); - if (token is null) - { - context.Reject( - error: Errors.InvalidToken, - description: SR.GetResourceString(SR.ID2019), - uri: SR.FormatID8000(SR.ID2019)); - - return; - } + var token = await _tokenManager.FindByIdAsync(context.TokenId) ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0021)); if (await _tokenManager.HasStatusAsync(token, Statuses.Redeemed)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6002), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6002), context.TokenId); context.Reject( error: Errors.InvalidToken, @@ -628,7 +636,7 @@ public static partial class OpenIddictClientHandlers if (!await _tokenManager.HasStatusAsync(token, Statuses.Valid)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), context.TokenId); context.Reject( error: Errors.InvalidToken, @@ -637,13 +645,6 @@ public static partial class OpenIddictClientHandlers return; } - - // Restore the creation/expiration dates/identifiers from the token entry metadata. - context.Principal.SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) - .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token)) - .SetTokenId(await _tokenManager.GetIdAsync(token)) - .SetTokenType(await _tokenManager.GetTypeAsync(token)); } } @@ -853,16 +854,16 @@ public static partial class OpenIddictClientHandlers } /// - /// Contains the logic responsible for converting the token to a reference token. + /// Contains the logic responsible for attaching the token payload to the token entry. /// Note: this handler is not used when token storage is disabled. /// - public sealed class ConvertReferenceToken : IOpenIddictClientHandler + public sealed class AttachTokenPayload : IOpenIddictClientHandler { private readonly IOpenIddictTokenManager _tokenManager; - public ConvertReferenceToken() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0318)); + public AttachTokenPayload() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0318)); - public ConvertReferenceToken(IOpenIddictTokenManager tokenManager) + public AttachTokenPayload(IOpenIddictTokenManager tokenManager) => _tokenManager = tokenManager ?? throw new ArgumentNullException(nameof(tokenManager)); /// @@ -872,7 +873,7 @@ public static partial class OpenIddictClientHandlers = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() - .UseScopedHandler() + .UseScopedHandler() .SetOrder(GenerateIdentityModelToken.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -900,14 +901,22 @@ public static partial class OpenIddictClientHandlers // Attach the generated token to the token entry. descriptor.Payload = context.Token; descriptor.Principal = context.Principal; - descriptor.ReferenceId = Base64UrlEncoder.Encode(OpenIddictHelpers.CreateRandomArray(size: 256)); + + if (context.IsReferenceToken) + { + descriptor.ReferenceId = Base64UrlEncoder.Encode(OpenIddictHelpers.CreateRandomArray(size: 256)); + } await _tokenManager.UpdateAsync(token, descriptor); - // Replace the returned token by the reference identifier. - context.Token = descriptor.ReferenceId; + context.Logger.LogTrace(SR.GetResourceString(SR.ID6014), context.Token, identifier, context.TokenType); - context.Logger.LogTrace(SR.GetResourceString(SR.ID6014), context.TokenType, identifier, descriptor.ReferenceId); + // Replace the returned token by the reference identifier, if applicable. + if (context.IsReferenceToken) + { + context.Token = descriptor.ReferenceId; + context.Logger.LogTrace(SR.GetResourceString(SR.ID6015), descriptor.ReferenceId, identifier, context.TokenType); + } } } } diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 8dabf053..a846ad5a 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -2258,6 +2258,7 @@ public static partial class OpenIddictClientHandlers var notification = new GenerateTokenContext(context.Transaction) { CreateTokenEntry = false, + IsReferenceToken = false, PersistTokenPayload = false, Principal = context.ClientAssertionTokenPrincipal!, TokenFormat = TokenFormats.Jwt, @@ -4624,6 +4625,7 @@ public static partial class OpenIddictClientHandlers var notification = new GenerateTokenContext(context.Transaction) { CreateTokenEntry = !context.Options.DisableTokenStorage, + IsReferenceToken = !context.Options.DisableTokenStorage, PersistTokenPayload = !context.Options.DisableTokenStorage, Principal = context.StateTokenPrincipal!, TokenFormat = TokenFormats.Jwt, @@ -5215,6 +5217,7 @@ public static partial class OpenIddictClientHandlers var notification = new GenerateTokenContext(context.Transaction) { CreateTokenEntry = !context.Options.DisableTokenStorage, + IsReferenceToken = !context.Options.DisableTokenStorage, PersistTokenPayload = !context.Options.DisableTokenStorage, Principal = context.StateTokenPrincipal!, TokenFormat = TokenFormats.Jwt, diff --git a/src/OpenIddict.Client/OpenIddictClientOptions.cs b/src/OpenIddict.Client/OpenIddictClientOptions.cs index 9c4cd7c2..b73097f1 100644 --- a/src/OpenIddict.Client/OpenIddictClientOptions.cs +++ b/src/OpenIddict.Client/OpenIddictClientOptions.cs @@ -120,8 +120,7 @@ public sealed class OpenIddictClientOptions /// /// Gets or sets a boolean indicating whether token storage should be disabled. /// When disabled, no database entry is created for the tokens created by the - /// OpenIddict client services. Using this option is generally NOT recommended - /// as it prevents the tokens from being revoked (if needed). + /// OpenIddict client services. Using this option is generally NOT recommended. /// public bool DisableTokenStorage { get; set; } diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs index 98ada848..9d85865f 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs @@ -189,31 +189,31 @@ public static partial class OpenIddictServerDataProtectionHandlers // // Note: reference tokens are encrypted using a different "purpose" string than non-reference tokens. var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector( - (type, context.TokenId) switch + (type, context.IsReferenceToken) switch { - (TokenTypeHints.AccessToken, { Length: not 0 }) + (TokenTypeHints.AccessToken, true) => new[] { Handlers.Server, Formats.AccessToken, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.AccessToken, null or { Length: 0 }) + (TokenTypeHints.AccessToken, false) => new[] { Handlers.Server, Formats.AccessToken, Schemes.Server }, - (TokenTypeHints.AuthorizationCode, { Length: not 0 }) + (TokenTypeHints.AuthorizationCode, true) => new[] { Handlers.Server, Formats.AuthorizationCode, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.AuthorizationCode, null or { Length: 0 }) + (TokenTypeHints.AuthorizationCode, false) => new[] { Handlers.Server, Formats.AuthorizationCode, Schemes.Server }, - (TokenTypeHints.DeviceCode, { Length: not 0 }) + (TokenTypeHints.DeviceCode, true) => new[] { Handlers.Server, Formats.DeviceCode, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.DeviceCode, null or { Length: 0 }) + (TokenTypeHints.DeviceCode, false) => new[] { Handlers.Server, Formats.DeviceCode, Schemes.Server }, - (TokenTypeHints.RefreshToken, { Length: not 0 }) + (TokenTypeHints.RefreshToken, true) => new[] { Handlers.Server, Formats.RefreshToken, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.RefreshToken, null or { Length: 0 }) + (TokenTypeHints.RefreshToken, false) => new[] { Handlers.Server, Formats.RefreshToken, Schemes.Server }, - (TokenTypeHints.UserCode, { Length: not 0 }) + (TokenTypeHints.UserCode, true) => new[] { Handlers.Server, Formats.UserCode, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.UserCode, null or { Length: 0 }) + (TokenTypeHints.UserCode, false) => new[] { Handlers.Server, Formats.UserCode, Schemes.Server }, _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0003)) @@ -337,7 +337,7 @@ public static partial class OpenIddictServerDataProtectionHandlers // // Note: reference tokens are encrypted using a different "purpose" string than non-reference tokens. var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector( - (context.TokenType, context.PersistTokenPayload) switch + (context.TokenType, context.IsReferenceToken) switch { (TokenTypeHints.AccessToken, true) => new[] { Handlers.Server, Formats.AccessToken, Features.ReferenceTokens, Schemes.Server }, @@ -374,7 +374,7 @@ public static partial class OpenIddictServerDataProtectionHandlers context.Token = Base64UrlEncoder.Encode(protector.Protect(buffer.ToArray())); - context.Logger.LogTrace(SR.GetResourceString(SR.ID6013), context.TokenType, + context.Logger.LogTrace(SR.GetResourceString(SR.ID6016), context.TokenType, context.Token, context.Principal.Claims); return default; diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs b/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs index 04872f37..ca4b2ed2 100644 --- a/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerEvents.Protection.cs @@ -46,6 +46,12 @@ public static partial class OpenIddictServerEvents /// public bool CreateTokenEntry { get; set; } + /// + /// Gets or sets a boolean indicating whether a reference token should be used + /// and, if applicable, returned to the caller instead of the actual token payload. + /// + public bool IsReferenceToken { get; set; } + /// /// Gets or sets a boolean indicating whether the token payload /// should be persisted alongside the token metadata in the database. @@ -135,6 +141,16 @@ public static partial class OpenIddictServerEvents /// public string? TokenTypeHint { get; set; } = default!; + /// + /// Gets or sets a boolean indicating whether the validated token is a reference token. + /// + public bool IsReferenceToken { get; set; } + + /// + /// Gets or sets the authorization entry identifier associated with the token, if applicable. + /// + public string? AuthorizationId { get; set; } + /// /// Gets or sets the token entry identifier associated with the token, if applicable. /// diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs index 612389ae..c1c3f2a3 100644 --- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs +++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs @@ -44,6 +44,7 @@ public static class OpenIddictServerExtensions builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); @@ -72,6 +73,7 @@ public static class OpenIddictServerExtensions builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); diff --git a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs index 8b221a5c..f905a1c7 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs @@ -75,6 +75,22 @@ public static class OpenIddictServerHandlerFilters } } + /// + /// Represents a filter that excludes the associated handlers if no authorization identifier is resolved from the token. + /// + public sealed class RequireAuthorizationIdResolved : IOpenIddictServerHandlerFilter + { + public ValueTask IsActiveAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(!string.IsNullOrEmpty(context.AuthorizationId)); + } + } + /// /// Represents a filter that excludes the associated handlers if the request is not an authorization request. /// @@ -507,6 +523,22 @@ public static class OpenIddictServerHandlerFilters } } + /// + /// Represents a filter that excludes the associated handlers if no token identifier is resolved from the token. + /// + public sealed class RequireTokenIdResolved : IOpenIddictServerHandlerFilter + { + public ValueTask IsActiveAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(!string.IsNullOrEmpty(context.TokenId)); + } + } + /// /// Represents a filter that excludes the associated handlers if no token entry is created in the database. /// diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index c3503c8c..b2dc69b6 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -29,7 +29,7 @@ public static partial class OpenIddictServerHandlers ValidateIdentityModelToken.Descriptor, NormalizeScopeClaims.Descriptor, MapInternalClaims.Descriptor, - RestoreReferenceTokenProperties.Descriptor, + RestoreTokenEntryProperties.Descriptor, ValidatePrincipal.Descriptor, ValidateExpirationDate.Descriptor, ValidateTokenEntry.Descriptor, @@ -41,7 +41,7 @@ public static partial class OpenIddictServerHandlers AttachSecurityCredentials.Descriptor, CreateTokenEntry.Descriptor, GenerateIdentityModelToken.Descriptor, - ConvertReferenceToken.Descriptor, + AttachTokenPayload.Descriptor, BeautifyToken.Descriptor); /// @@ -231,6 +231,7 @@ public static partial class OpenIddictServerHandlers // Replace the token parameter by the payload resolved from the token entry // and store the identifier of the reference token so it can be later // used to restore the properties associated with the token. + context.IsReferenceToken = true; context.Token = payload; context.TokenId = await _tokenManager.GetIdAsync(token); @@ -555,16 +556,16 @@ public static partial class OpenIddictServerHandlers } /// - /// Contains the logic responsible for restoring the properties associated with a reference token entry. + /// Contains the logic responsible for restoring the properties associated with a token entry. /// Note: this handler is not used when the degraded mode is enabled. /// - public sealed class RestoreReferenceTokenProperties : IOpenIddictServerHandler + public sealed class RestoreTokenEntryProperties : IOpenIddictServerHandler { private readonly IOpenIddictTokenManager _tokenManager; - public RestoreReferenceTokenProperties() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)); + public RestoreTokenEntryProperties() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)); - public RestoreReferenceTokenProperties(IOpenIddictTokenManager tokenManager) + public RestoreTokenEntryProperties(IOpenIddictTokenManager tokenManager) => _tokenManager = tokenManager ?? throw new ArgumentNullException(nameof(tokenManager)); /// @@ -574,7 +575,7 @@ public static partial class OpenIddictServerHandlers = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() - .UseScopedHandler() + .UseScopedHandler() .SetOrder(MapInternalClaims.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); @@ -586,20 +587,54 @@ public static partial class OpenIddictServerHandlers throw new ArgumentNullException(nameof(context)); } - if (context.Principal is null || string.IsNullOrEmpty(context.TokenId)) + if (context.Principal is null) { return; } - var token = await _tokenManager.FindByIdAsync(context.TokenId) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0021)); + // Extract the token identifier from the authentication principal. + // + // If no token identifier can be found, this indicates that the token + // has no backing database entry (e.g if token storage was disabled). + var identifier = context.Principal.GetTokenId(); + if (string.IsNullOrEmpty(identifier)) + { + return; + } + + // If the token entry cannot be found, return a generic error. + var token = await _tokenManager.FindByIdAsync(identifier); + if (token is null) + { + context.Reject( + error: Errors.InvalidToken, + description: context.Principal.GetTokenType() switch + { + TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2001), + TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2002), + TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2003), + + _ => SR.GetResourceString(SR.ID2004) + }, + uri: context.Principal.GetTokenType() switch + { + TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2001), + TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2002), + TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2003), + + _ => SR.FormatID8000(SR.ID2004) + }); + + return; + } // Restore the creation/expiration dates/identifiers from the token entry metadata. - context.Principal.SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) - .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token)) - .SetTokenId(await _tokenManager.GetIdAsync(token)) - .SetTokenType(await _tokenManager.GetTypeAsync(token)); + context.Principal + .SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) + .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) + .SetAuthorizationId(context.AuthorizationId = await _tokenManager.GetAuthorizationIdAsync(token)) + .SetTokenId(context.TokenId = await _tokenManager.GetIdAsync(token)) + .SetTokenType(await _tokenManager.GetTypeAsync(token)); } } @@ -614,7 +649,7 @@ public static partial class OpenIddictServerHandlers public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .UseSingletonHandler() - .SetOrder(RestoreReferenceTokenProperties.Descriptor.Order + 1_000) + .SetOrder(RestoreTokenEntryProperties.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); @@ -760,6 +795,7 @@ public static partial class OpenIddictServerHandlers = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() + .AddFilter() .UseScopedHandler() .SetOrder(ValidateExpirationDate.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) @@ -773,41 +809,10 @@ public static partial class OpenIddictServerHandlers } Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); + Debug.Assert(!string.IsNullOrEmpty(context.TokenId), SR.GetResourceString(SR.ID4017)); - // Extract the token identifier from the authentication principal. - // If no token identifier can be found, this indicates that the token - // has no backing database entry (e.g an access token or an identity token). - var identifier = context.Principal.GetTokenId(); - if (string.IsNullOrEmpty(identifier)) - { - return; - } - - // If the token entry cannot be found, return a generic error. - var token = await _tokenManager.FindByIdAsync(identifier); - if (token is null) - { - context.Reject( - error: Errors.InvalidToken, - description: context.Principal.GetTokenType() switch - { - TokenTypeHints.AuthorizationCode => SR.GetResourceString(SR.ID2001), - TokenTypeHints.DeviceCode => SR.GetResourceString(SR.ID2002), - TokenTypeHints.RefreshToken => SR.GetResourceString(SR.ID2003), - - _ => SR.GetResourceString(SR.ID2004) - }, - uri: context.Principal.GetTokenType() switch - { - TokenTypeHints.AuthorizationCode => SR.FormatID8000(SR.ID2001), - TokenTypeHints.DeviceCode => SR.FormatID8000(SR.ID2002), - TokenTypeHints.RefreshToken => SR.FormatID8000(SR.ID2003), - - _ => SR.FormatID8000(SR.ID2004) - }); - - return; - } + var token = await _tokenManager.FindByIdAsync(context.TokenId) ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0021)); // If the token is already marked as redeemed, this may indicate that it was compromised. // In this case, revoke the entire chain of tokens associated with the authorization. @@ -818,7 +823,7 @@ public static partial class OpenIddictServerHandlers { if (!context.Principal.HasTokenType(TokenTypeHints.RefreshToken) || !await IsReusableAsync(token)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6002), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6002), context.TokenId); context.Reject( error: Errors.InvalidToken, @@ -840,7 +845,7 @@ public static partial class OpenIddictServerHandlers }); // Revoke all the token entries associated with the authorization. - await TryRevokeChainAsync(await _tokenManager.GetAuthorizationIdAsync(token)); + await TryRevokeChainAsync(context.AuthorizationId); return; } @@ -851,7 +856,7 @@ public static partial class OpenIddictServerHandlers // If the token is not marked as valid yet, return an authorization_pending error. if (await _tokenManager.HasStatusAsync(token, Statuses.Inactive)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6003), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6003), context.TokenId); context.Reject( error: Errors.AuthorizationPending, @@ -864,7 +869,7 @@ public static partial class OpenIddictServerHandlers // If the token is marked as rejected, return an access_denied error. if (await _tokenManager.HasStatusAsync(token, Statuses.Rejected)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6004), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6004), context.TokenId); context.Reject( error: Errors.AccessDenied, @@ -876,7 +881,7 @@ public static partial class OpenIddictServerHandlers if (!await _tokenManager.HasStatusAsync(token, Statuses.Valid)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), context.TokenId); context.Reject( error: Errors.InvalidToken, @@ -900,13 +905,6 @@ public static partial class OpenIddictServerHandlers return; } - // Restore the creation/expiration dates/identifiers from the token entry metadata. - context.Principal.SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) - .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token)) - .SetTokenId(await _tokenManager.GetIdAsync(token)) - .SetTokenType(await _tokenManager.GetTypeAsync(token)); - async ValueTask IsReusableAsync(object token) { // If the reuse leeway was set to null, return false to indicate @@ -963,6 +961,7 @@ public static partial class OpenIddictServerHandlers = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() + .AddFilter() .UseScopedHandler() .SetOrder(ValidateTokenEntry.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) @@ -976,17 +975,12 @@ public static partial class OpenIddictServerHandlers } Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); + Debug.Assert(!string.IsNullOrEmpty(context.AuthorizationId), SR.GetResourceString(SR.ID4018)); - var identifier = context.Principal.GetAuthorizationId(); - if (string.IsNullOrEmpty(identifier)) - { - return; - } - - var authorization = await _authorizationManager.FindByIdAsync(identifier); + var authorization = await _authorizationManager.FindByIdAsync(context.AuthorizationId); if (authorization is null || !await _authorizationManager.HasStatusAsync(authorization, Statuses.Valid)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6006), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6006), context.AuthorizationId); context.Reject( error: Errors.InvalidToken, @@ -1144,7 +1138,7 @@ public static partial class OpenIddictServerHandlers var identifier = await _tokenManager.GetIdAsync(token); - // Attach the token identifier to the principal so that it can be stored in the token. + // Attach the token identifier to the principal so that it can be stored in the token payload. context.Principal.SetTokenId(identifier); context.Logger.LogTrace(SR.GetResourceString(SR.ID6012), context.TokenType, identifier); @@ -1285,16 +1279,16 @@ public static partial class OpenIddictServerHandlers } /// - /// Contains the logic responsible for converting the token to a reference token. + /// Contains the logic responsible for attaching the token payload to the token entry. /// Note: this handler is not used when the degraded mode is enabled. /// - public sealed class ConvertReferenceToken : IOpenIddictServerHandler + public sealed class AttachTokenPayload : IOpenIddictServerHandler { private readonly IOpenIddictTokenManager _tokenManager; - public ConvertReferenceToken() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)); + public AttachTokenPayload() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)); - public ConvertReferenceToken(IOpenIddictTokenManager tokenManager) + public AttachTokenPayload(IOpenIddictTokenManager tokenManager) => _tokenManager = tokenManager ?? throw new ArgumentNullException(nameof(tokenManager)); /// @@ -1305,7 +1299,7 @@ public static partial class OpenIddictServerHandlers .AddFilter() .AddFilter() .AddFilter() - .UseScopedHandler() + .UseScopedHandler() .SetOrder(GenerateIdentityModelToken.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); @@ -1334,40 +1328,47 @@ public static partial class OpenIddictServerHandlers descriptor.Payload = context.Token; descriptor.Principal = context.Principal; - if (context.TokenType is TokenTypeHints.UserCode) + if (context.IsReferenceToken) { - do + if (context.TokenType is TokenTypeHints.UserCode) { - // Note: unlike other reference tokens, user codes are meant to be used by humans, - // who may have to enter it in a web form. To ensure they remain easy enough to type - // even by users with non-Latin keyboards, user codes generated by OpenIddict are - // only compound of 12 digits, generated using a crypto-secure random number generator. - // In this case, the resulting user code is estimated to have at most ~40 bits of entropy. + do + { + // Note: unlike other reference tokens, user codes are meant to be used by humans, + // who may have to enter it in a web form. To ensure they remain easy enough to type + // even by users with non-Latin keyboards, user codes generated by OpenIddict are + // only compound of 12 digits, generated using a crypto-secure random number generator. + // In this case, the resulting user code is estimated to have at most ~40 bits of entropy. - static string CreateRandomNumericCode(int length) => OpenIddictHelpers.CreateRandomString( - charset: stackalloc[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, - length: length); + static string CreateRandomNumericCode(int length) => OpenIddictHelpers.CreateRandomString( + charset: stackalloc[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }, + length: length); - descriptor.ReferenceId = CreateRandomNumericCode(length: 12); - } + descriptor.ReferenceId = CreateRandomNumericCode(length: 12); + } - // User codes are relatively short. To help reduce the risks of collisions with - // existing entries, a database check is performed here before updating the entry. - while (await _tokenManager.FindByReferenceIdAsync(descriptor.ReferenceId) is not null); - } + // User codes are relatively short. To help reduce the risks of collisions with + // existing entries, a database check is performed here before updating the entry. + while (await _tokenManager.FindByReferenceIdAsync(descriptor.ReferenceId) is not null); + } - else - { - // For other tokens, generate a base64url-encoded 256-bit random identifier. - descriptor.ReferenceId = Base64UrlEncoder.Encode(OpenIddictHelpers.CreateRandomArray(size: 256)); + else + { + // For other tokens, generate a base64url-encoded 256-bit random identifier. + descriptor.ReferenceId = Base64UrlEncoder.Encode(OpenIddictHelpers.CreateRandomArray(size: 256)); + } } await _tokenManager.UpdateAsync(token, descriptor); - // Replace the returned token by the reference identifier. - context.Token = descriptor.ReferenceId; + context.Logger.LogTrace(SR.GetResourceString(SR.ID6014), context.Token, identifier, context.TokenType); - context.Logger.LogTrace(SR.GetResourceString(SR.ID6014), context.TokenType, identifier, descriptor.ReferenceId); + // Replace the returned token by the reference identifier, if applicable. + if (context.IsReferenceToken) + { + context.Token = descriptor.ReferenceId; + context.Logger.LogTrace(SR.GetResourceString(SR.ID6015), descriptor.ReferenceId, identifier, context.TokenType); + } } } @@ -1387,7 +1388,7 @@ public static partial class OpenIddictServerHandlers // reference identifiers only works when the degraded mode is disabled. .AddFilter() .UseSingletonHandler() - .SetOrder(ConvertReferenceToken.Descriptor.Order + 1_000) + .SetOrder(AttachTokenPayload.Descriptor.Order + 1_000) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); @@ -1402,7 +1403,7 @@ public static partial class OpenIddictServerHandlers // To make user codes easier to read and type by humans, a dash is automatically // appended before each new block of 4 integers. These dashes are expected to be // stripped from the user codes when receiving them at the verification endpoint. - if (context.TokenType is TokenTypeHints.UserCode) + if (context.IsReferenceToken && context.TokenType is TokenTypeHints.UserCode) { var builder = new StringBuilder(context.Token); if (builder.Length % 4 != 0) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 9432d019..492b381c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -2412,6 +2412,7 @@ public static partial class OpenIddictServerHandlers CreateTokenEntry = !context.Options.DisableTokenStorage, // Access tokens can be converted to reference tokens if the // corresponding option was enabled in the server options. + IsReferenceToken = context.Options.UseReferenceAccessTokens, PersistTokenPayload = context.Options.UseReferenceAccessTokens, Principal = context.AccessTokenPrincipal!, TokenFormat = TokenFormats.Jwt, @@ -2478,6 +2479,7 @@ public static partial class OpenIddictServerHandlers { ClientId = context.ClientId, CreateTokenEntry = !context.Options.DisableTokenStorage, + IsReferenceToken = !context.Options.DisableTokenStorage, PersistTokenPayload = !context.Options.DisableTokenStorage, Principal = context.AuthorizationCodePrincipal!, TokenFormat = TokenFormats.Jwt, @@ -2543,8 +2545,16 @@ public static partial class OpenIddictServerHandlers var notification = new GenerateTokenContext(context.Transaction) { ClientId = context.ClientId, - CreateTokenEntry = !context.Options.DisableTokenStorage, - // Device codes can be converted to reference tokens if they are not generated + // Don't create a new entry if the device code is generated as part + // of a device code swap made by the user code verification endpoint. + CreateTokenEntry = context.EndpointType switch + { + OpenIddictServerEndpointType.Verification => false, + + _ => !context.Options.DisableTokenStorage + }, + IsReferenceToken = !context.Options.DisableTokenStorage, + // Device codes are not persisted using the generic logic if they are generated // as part of a device code swap made by the user code verification endpoint. PersistTokenPayload = context.EndpointType switch { @@ -2554,7 +2564,7 @@ public static partial class OpenIddictServerHandlers }, Principal = context.DeviceCodePrincipal!, TokenFormat = TokenFormats.Jwt, - TokenType = TokenTypeHints.DeviceCode + TokenType = TokenTypeHints.DeviceCode, }; await _dispatcher.DispatchAsync(notification); @@ -2619,6 +2629,7 @@ public static partial class OpenIddictServerHandlers CreateTokenEntry = !context.Options.DisableTokenStorage, // Refresh tokens can be converted to reference tokens if the // corresponding option was enabled in the server options. + IsReferenceToken = context.Options.UseReferenceRefreshTokens, PersistTokenPayload = context.Options.UseReferenceRefreshTokens, Principal = context.RefreshTokenPrincipal!, TokenFormat = TokenFormats.Jwt, @@ -2738,7 +2749,7 @@ public static partial class OpenIddictServerHandlers throw new InvalidOperationException(SR.GetResourceString(SR.ID0020)); } - // Extract the token identifier from the authentication principal. + // Extract the device code identifier from the user code principal. var identifier = context.Principal.GetClaim(Claims.Private.DeviceCodeId); if (string.IsNullOrEmpty(identifier)) { @@ -2890,6 +2901,7 @@ public static partial class OpenIddictServerHandlers ClientId = context.ClientId, CreateTokenEntry = !context.Options.DisableTokenStorage, PersistTokenPayload = !context.Options.DisableTokenStorage, + IsReferenceToken = !context.Options.DisableTokenStorage, Principal = context.UserCodePrincipal!, TokenFormat = TokenFormats.Jwt, TokenType = TokenTypeHints.UserCode @@ -2955,7 +2967,8 @@ public static partial class OpenIddictServerHandlers { ClientId = context.ClientId, CreateTokenEntry = !context.Options.DisableTokenStorage, - // Identity tokens cannot never be reference tokens. + // Identity tokens cannot be reference tokens. + IsReferenceToken = false, PersistTokenPayload = false, Principal = context.IdentityTokenPrincipal!, TokenFormat = TokenFormats.Jwt, diff --git a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionHandlers.Protection.cs b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionHandlers.Protection.cs index e09ad676..9fbb13cf 100644 --- a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionHandlers.Protection.cs +++ b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionHandlers.Protection.cs @@ -98,11 +98,11 @@ public static partial class OpenIddictValidationDataProtectionHandlers // // Note: reference tokens are encrypted using a different "purpose" string than non-reference tokens. var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector( - (type, context.TokenId) switch + (type, context.IsReferenceToken) switch { - (TokenTypeHints.AccessToken, { Length: not 0 }) + (TokenTypeHints.AccessToken, true) => new[] { Handlers.Server, Formats.AccessToken, Features.ReferenceTokens, Schemes.Server }, - (TokenTypeHints.AccessToken, null or { Length: 0 }) + (TokenTypeHints.AccessToken, false) => new[] { Handlers.Server, Formats.AccessToken, Schemes.Server }, _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0003)) diff --git a/src/OpenIddict.Validation/OpenIddictValidationEvents.Protection.cs b/src/OpenIddict.Validation/OpenIddictValidationEvents.Protection.cs index 6d7f4edc..217504cf 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationEvents.Protection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationEvents.Protection.cs @@ -49,6 +49,16 @@ public static partial class OpenIddictValidationEvents /// public string Token { get; set; } = default!; + /// + /// Gets or sets a boolean indicating whether the validated token is a reference token. + /// + public bool IsReferenceToken { get; set; } + + /// + /// Gets or sets the authorization entry identifier associated with the token, if applicable. + /// + public string? AuthorizationId { get; set; } + /// /// Gets or sets the token entry identifier associated with the token, if applicable. /// diff --git a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs index ab9ff035..0107debe 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationExtensions.cs @@ -44,8 +44,10 @@ public static class OpenIddictValidationExtensions builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); // Note: TryAddEnumerable() is used here to ensure the initializer is registered only once. diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs index ea74b34f..6ab2580c 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs @@ -59,6 +59,22 @@ public static class OpenIddictValidationHandlerFilters } } + /// + /// Represents a filter that excludes the associated handlers if no authorization identifier is resolved from the token. + /// + public sealed class RequireAuthorizationIdResolved : IOpenIddictValidationHandlerFilter + { + public ValueTask IsActiveAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(!string.IsNullOrEmpty(context.AuthorizationId)); + } + } + /// /// Represents a filter that excludes the associated handlers if local validation is not used. /// @@ -91,6 +107,22 @@ public static class OpenIddictValidationHandlerFilters } } + /// + /// Represents a filter that excludes the associated handlers if no token identifier is resolved from the token. + /// + public sealed class RequireTokenIdResolved : IOpenIddictValidationHandlerFilter + { + public ValueTask IsActiveAsync(ValidateTokenContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(!string.IsNullOrEmpty(context.TokenId)); + } + } + /// /// Represents a filter that excludes the associated handlers if token validation was not enabled. /// diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs index dbc08b76..f9b0bab8 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs @@ -28,7 +28,7 @@ public static partial class OpenIddictValidationHandlers IntrospectToken.Descriptor, NormalizeScopeClaims.Descriptor, MapInternalClaims.Descriptor, - RestoreReferenceTokenProperties.Descriptor, + RestoreTokenEntryProperties.Descriptor, ValidatePrincipal.Descriptor, ValidateExpirationDate.Descriptor, ValidateAudience.Descriptor, @@ -189,6 +189,7 @@ public static partial class OpenIddictValidationHandlers // Replace the token parameter by the payload resolved from the token entry // and store the identifier of the reference token so it can be later // used to restore the properties associated with the token. + context.IsReferenceToken = true; context.Token = payload; context.TokenId = await _tokenManager.GetIdAsync(token); } @@ -533,16 +534,16 @@ public static partial class OpenIddictValidationHandlers } /// - /// Contains the logic responsible for restoring the properties associated with a reference token entry. + /// Contains the logic responsible for restoring the properties associated with a token entry. /// Note: this handler is not used when the degraded mode is enabled. /// - public sealed class RestoreReferenceTokenProperties : IOpenIddictValidationHandler + public sealed class RestoreTokenEntryProperties : IOpenIddictValidationHandler { private readonly IOpenIddictTokenManager _tokenManager; - public RestoreReferenceTokenProperties() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0139)); + public RestoreTokenEntryProperties() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0139)); - public RestoreReferenceTokenProperties(IOpenIddictTokenManager tokenManager) + public RestoreTokenEntryProperties(IOpenIddictTokenManager tokenManager) => _tokenManager = tokenManager ?? throw new ArgumentNullException(nameof(tokenManager)); /// @@ -552,7 +553,7 @@ public static partial class OpenIddictValidationHandlers = OpenIddictValidationHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() - .UseScopedHandler() + .UseScopedHandler() .SetOrder(MapInternalClaims.Descriptor.Order + 1_000) .SetType(OpenIddictValidationHandlerType.BuiltIn) .Build(); @@ -565,20 +566,40 @@ public static partial class OpenIddictValidationHandlers throw new ArgumentNullException(nameof(context)); } - if (context.Principal is null || string.IsNullOrEmpty(context.TokenId)) + if (context.Principal is null) { return; } - var token = await _tokenManager.FindByIdAsync(context.TokenId) ?? - throw new InvalidOperationException(SR.GetResourceString(SR.ID0021)); + // Extract the token identifier from the authentication principal. + // + // If no token identifier can be found, this indicates that the token + // has no backing database entry (e.g if token storage was disabled). + var identifier = context.Principal.GetTokenId(); + if (string.IsNullOrEmpty(identifier)) + { + return; + } + + // If the token entry cannot be found, return a generic error. + var token = await _tokenManager.FindByIdAsync(identifier); + if (token is null) + { + context.Reject( + error: Errors.InvalidToken, + description: SR.GetResourceString(SR.ID2019), + uri: SR.FormatID8000(SR.ID2019)); + + return; + } // Restore the creation/expiration dates/identifiers from the token entry metadata. - context.Principal.SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) - .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token)) - .SetTokenId(await _tokenManager.GetIdAsync(token)) - .SetTokenType(await _tokenManager.GetTypeAsync(token)); + context.Principal + .SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) + .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) + .SetAuthorizationId(context.AuthorizationId = await _tokenManager.GetAuthorizationIdAsync(token)) + .SetTokenId(context.TokenId = await _tokenManager.GetIdAsync(token)) + .SetTokenType(await _tokenManager.GetTypeAsync(token)); } } @@ -593,7 +614,7 @@ public static partial class OpenIddictValidationHandlers public static OpenIddictValidationHandlerDescriptor Descriptor { get; } = OpenIddictValidationHandlerDescriptor.CreateBuilder() .UseSingletonHandler() - .SetOrder(RestoreReferenceTokenProperties.Descriptor.Order + 1_000) + .SetOrder(RestoreTokenEntryProperties.Descriptor.Order + 1_000) .SetType(OpenIddictValidationHandlerType.BuiltIn) .Build(); @@ -761,6 +782,7 @@ public static partial class OpenIddictValidationHandlers = OpenIddictValidationHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() + .AddFilter() .UseScopedHandler() .SetOrder(ValidateAudience.Descriptor.Order + 1_000) .SetType(OpenIddictValidationHandlerType.BuiltIn) @@ -775,17 +797,14 @@ public static partial class OpenIddictValidationHandlers } Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); + Debug.Assert(!string.IsNullOrEmpty(context.TokenId), SR.GetResourceString(SR.ID4017)); - var identifier = context.Principal.GetTokenId(); - if (string.IsNullOrEmpty(identifier)) - { - return; - } + var token = await _tokenManager.FindByIdAsync(context.TokenId) ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0021)); - var token = await _tokenManager.FindByIdAsync(identifier); - if (token is null || !await _tokenManager.HasStatusAsync(token, Statuses.Valid)) + if (!await _tokenManager.HasStatusAsync(token, Statuses.Valid)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6005), context.TokenId); context.Reject( error: Errors.InvalidToken, @@ -794,13 +813,6 @@ public static partial class OpenIddictValidationHandlers return; } - - // Restore the creation/expiration dates/identifiers from the token entry metadata. - context.Principal.SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) - .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token)) - .SetTokenId(await _tokenManager.GetIdAsync(token)) - .SetTokenType(await _tokenManager.GetTypeAsync(token)); } } @@ -825,6 +837,7 @@ public static partial class OpenIddictValidationHandlers = OpenIddictValidationHandlerDescriptor.CreateBuilder() .AddFilter() .AddFilter() + .AddFilter() .UseScopedHandler() .SetOrder(ValidateTokenEntry.Descriptor.Order + 1_000) .SetType(OpenIddictValidationHandlerType.BuiltIn) @@ -839,17 +852,12 @@ public static partial class OpenIddictValidationHandlers } Debug.Assert(context.Principal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); + Debug.Assert(!string.IsNullOrEmpty(context.AuthorizationId), SR.GetResourceString(SR.ID4018)); - var identifier = context.Principal.GetAuthorizationId(); - if (string.IsNullOrEmpty(identifier)) - { - return; - } - - var authorization = await _authorizationManager.FindByIdAsync(identifier); + var authorization = await _authorizationManager.FindByIdAsync(context.AuthorizationId); if (authorization is null || !await _authorizationManager.HasStatusAsync(authorization, Statuses.Valid)) { - context.Logger.LogInformation(SR.GetResourceString(SR.ID6006), identifier); + context.Logger.LogInformation(SR.GetResourceString(SR.ID6006), context.AuthorizationId); context.Reject( error: Errors.InvalidToken, diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs index 976d7ec4..0f34537c 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs @@ -530,6 +530,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.FindByReferenceIdAsync("g43LaWCUrz2RaLILz2L1bg1bOpMSv1hGrH12IIkB9H4", It.IsAny())) .ReturnsAsync(token); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.DeviceCode); + mock.Setup(manager => manager.HasTypeAsync(token, TokenTypeHints.DeviceCode, It.IsAny())) .ReturnsAsync(true); @@ -559,7 +562,9 @@ public abstract partial class OpenIddictServerIntegrationTests Assert.Equal(new[] { TokenTypeHints.DeviceCode }, context.ValidTokenTypes); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetClaim(Claims.Subject, "Bob le Bricoleur"); + .SetClaim(Claims.Subject, "Bob le Bricoleur") + .SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103") + .SetTokenType(TokenTypeHints.DeviceCode); return default; }); @@ -2365,6 +2370,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.AuthorizationCode); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); }); @@ -2448,6 +2456,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -2522,6 +2533,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -2596,6 +2610,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -2683,6 +2700,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); + mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) + .ReturnsAsync(TokenTypeHints.AuthorizationCode); + mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -2784,6 +2804,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); + mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -2877,6 +2900,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); + mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -2970,6 +2996,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetAuthorizationIdAsync(tokens[0], It.IsAny())) .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0"); + mock.Setup(manager => manager.GetTypeAsync(tokens[0], It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(tokens[0], Statuses.Redeemed, It.IsAny())) .ReturnsAsync(true); @@ -3062,6 +3091,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.AuthorizationCode); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); @@ -3149,6 +3181,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); @@ -3270,6 +3305,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.AuthorizationCode); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); @@ -3362,6 +3400,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); @@ -3881,6 +3922,11 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("0270F515-C5B1-4FBF-B673-D7CAF7CCDABC"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(flow is GrantTypes.AuthorizationCode ? + TokenTypeHints.AuthorizationCode : + TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs index bb09a511..01e2cb4a 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs @@ -3057,6 +3057,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.AuthorizationCode); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Valid, It.IsAny())) .ReturnsAsync(true); @@ -3132,6 +3135,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103"); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); @@ -3198,6 +3204,9 @@ public abstract partial class OpenIddictServerIntegrationTests mock.Setup(manager => manager.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny())) .ReturnsAsync(token); + mock.Setup(manager => manager.GetTypeAsync(token, It.IsAny())) + .ReturnsAsync(TokenTypeHints.RefreshToken); + mock.Setup(manager => manager.HasStatusAsync(token, Statuses.Redeemed, It.IsAny())) .ReturnsAsync(false); From 773594231ea19c8dfef0fb17124f42c1b58c8a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 12 Dec 2022 22:51:40 +0100 Subject: [PATCH 04/10] Prevent enabling the device authorization flow when token storage is disabled (unless the degraded mode is used) --- .../OpenIddictResources.resx | 3 ++ .../OpenIddictServerConfiguration.cs | 11 ++++++-- ...ctServerIntegrationTests.Authentication.cs | 13 +++++++++ ...OpenIddictServerIntegrationTests.Device.cs | 28 +++++++++++++++++-- ...enIddictServerIntegrationTests.Exchange.cs | 10 +++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index a4a1d0e6..1cfb795b 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1393,6 +1393,9 @@ Consider registering a certificate using 'services.AddOpenIddict().AddClient().A The '{0}' parameter cannot be null or empty. + + The device authorization flow cannot be enabled when token storage is disabled (unless the degraded mode is used). + The security token is missing. diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index 55a6f477..1d8d7327 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -138,6 +138,13 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions credentials.Key is X509SecurityKey x509SecurityKey && + if (options.EncryptionCredentials.TrueForAll(static credentials => credentials.Key is X509SecurityKey x509SecurityKey && (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0087)); } // If all the registered signing credentials are backed by a X.509 certificate, at least one of them must be valid. - if (options.SigningCredentials.TrueForAll(credentials => credentials.Key is X509SecurityKey x509SecurityKey && + if (options.SigningCredentials.TrueForAll(static credentials => credentials.Key is X509SecurityKey x509SecurityKey && (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0088)); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs index d698c31d..05f33f57 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs @@ -832,7 +832,9 @@ public abstract partial class OpenIddictServerIntegrationTests var scope = new OpenIddictScope(); options.RegisterScopes("scope_registered_in_options"); + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); @@ -1445,6 +1447,9 @@ public abstract partial class OpenIddictServerIntegrationTests { options.Services.AddSingleton(manager); + options.SetDeviceEndpointUris(Array.Empty()); + options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableAuthorizationStorage(); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); @@ -1695,7 +1700,9 @@ public abstract partial class OpenIddictServerIntegrationTests await using var server = await CreateServerAsync(options => { + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableAuthorizationStorage(); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); @@ -1753,7 +1760,9 @@ public abstract partial class OpenIddictServerIntegrationTests await using var server = await CreateServerAsync(options => { + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableAuthorizationStorage(); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); @@ -1811,7 +1820,9 @@ public abstract partial class OpenIddictServerIntegrationTests await using var server = await CreateServerAsync(options => { + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableAuthorizationStorage(); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); @@ -1935,7 +1946,9 @@ public abstract partial class OpenIddictServerIntegrationTests await using var server = await CreateServerAsync(options => { + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableAuthorizationStorage(); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs index b02fdbc6..c7b58342 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs @@ -230,6 +230,7 @@ public abstract partial class OpenIddictServerIntegrationTests { // Arrange var application = new OpenIddictApplication(); + var token = new OpenIddictToken(); await using var server = await CreateServerAsync(options => { @@ -242,10 +243,21 @@ public abstract partial class OpenIddictServerIntegrationTests .ReturnsAsync(true); })); + options.Services.AddSingleton(CreateTokenManager(mock => + { + mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(token); + + mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) + .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + + mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) + .ReturnsAsync(token); + })); + options.RegisterScopes("registered_scope"); options.SetRevocationEndpointUris(Array.Empty()); options.DisableAuthorizationStorage(); - options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); options.AddEventHandler(builder => @@ -280,6 +292,7 @@ public abstract partial class OpenIddictServerIntegrationTests // Arrange var application = new OpenIddictApplication(); var scope = new OpenIddictScope(); + var token = new OpenIddictToken(); var manager = CreateScopeManager(mock => { @@ -303,10 +316,21 @@ public abstract partial class OpenIddictServerIntegrationTests .ReturnsAsync(true); })); + options.Services.AddSingleton(CreateTokenManager(mock => + { + mock.Setup(manager => manager.CreateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(token); + + mock.Setup(manager => manager.GetIdAsync(token, It.IsAny())) + .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + + mock.Setup(manager => manager.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) + .ReturnsAsync(token); + })); + options.RegisterScopes("scope_registered_in_options"); options.SetRevocationEndpointUris(Array.Empty()); options.DisableAuthorizationStorage(); - options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); options.Services.AddSingleton(manager); diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs index 0f34537c..afedea8e 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs @@ -1335,7 +1335,9 @@ public abstract partial class OpenIddictServerIntegrationTests await using var server = await CreateServerAsync(options => { options.RegisterScopes("scope_registered_in_options"); + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); @@ -1909,7 +1911,9 @@ public abstract partial class OpenIddictServerIntegrationTests options.Services.AddSingleton(manager); + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); @@ -1977,7 +1981,9 @@ public abstract partial class OpenIddictServerIntegrationTests options.Services.AddSingleton(manager); + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); @@ -2153,7 +2159,9 @@ public abstract partial class OpenIddictServerIntegrationTests .ReturnsAsync(true); })); + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); @@ -2206,7 +2214,9 @@ public abstract partial class OpenIddictServerIntegrationTests return default; })); + options.SetDeviceEndpointUris(Array.Empty()); options.SetRevocationEndpointUris(Array.Empty()); + options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode)); options.DisableTokenStorage(); options.DisableSlidingRefreshTokenExpiration(); }); From 0baa0e8399738b578bbde6874977f05c3bd7e50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 12 Dec 2022 23:30:58 +0100 Subject: [PATCH 05/10] Remove the unnecessary ValidateTokenContext.AuthorizationId property --- .../OpenIddictClientEvents.Protection.cs | 5 ----- .../OpenIddictClientExtensions.cs | 1 - .../OpenIddictClientHandlerFilters.cs | 16 ---------------- .../OpenIddictClientHandlers.Protection.cs | 2 -- 4 files changed, 24 deletions(-) diff --git a/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs b/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs index 9754af17..4b116219 100644 --- a/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientEvents.Protection.cs @@ -140,11 +140,6 @@ public static partial class OpenIddictClientEvents /// public bool IsReferenceToken { get; set; } - /// - /// Gets or sets the authorization entry identifier associated with the token, if applicable. - /// - public string? AuthorizationId { get; set; } - /// /// Gets or sets the token entry identifier associated with the token, if applicable. /// diff --git a/src/OpenIddict.Client/OpenIddictClientExtensions.cs b/src/OpenIddict.Client/OpenIddictClientExtensions.cs index 348e4e08..8b317b93 100644 --- a/src/OpenIddict.Client/OpenIddictClientExtensions.cs +++ b/src/OpenIddict.Client/OpenIddictClientExtensions.cs @@ -37,7 +37,6 @@ public static class OpenIddictClientExtensions // Register the built-in filters used by the default OpenIddict client event handlers. builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); diff --git a/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs b/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs index 0a1825f6..d8e907e4 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs @@ -27,22 +27,6 @@ public static class OpenIddictClientHandlerFilters } } - /// - /// Represents a filter that excludes the associated handlers if no authorization identifier is resolved from the token. - /// - public sealed class RequireAuthorizationIdResolved : IOpenIddictClientHandlerFilter - { - public ValueTask IsActiveAsync(ValidateTokenContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - - return new(!string.IsNullOrEmpty(context.AuthorizationId)); - } - } - /// /// Represents a filter that excludes the associated handlers if no backchannel access token is validated. /// diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs index 35d9ef66..6ab4839e 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs @@ -8,7 +8,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; using System.Security.Claims; -using System.Security.Cryptography; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; @@ -474,7 +473,6 @@ public static partial class OpenIddictClientHandlers context.Principal .SetCreationDate(await _tokenManager.GetCreationDateAsync(token)) .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token)) - .SetAuthorizationId(context.AuthorizationId = await _tokenManager.GetAuthorizationIdAsync(token)) .SetTokenId(context.TokenId = await _tokenManager.GetIdAsync(token)) .SetTokenType(await _tokenManager.GetTypeAsync(token)); } From 2f1ae5158da651ef1ad3623f1185d521b8cee801 Mon Sep 17 00:00:00 2001 From: Dan Hansen <16237377+danbluhmhansen@users.noreply.github.com> Date: Tue, 13 Dec 2022 15:40:03 +0100 Subject: [PATCH 06/10] Fix OpenIddictExtensions.AddClaims() to use JsonElement.ToString() instead of JsonElement.GetString() --- .../Primitives/OpenIddictExtensions.cs | 2 +- .../Primitives/OpenIddictExtensionsTests.cs | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs index 717c6fc7..b52f3d2c 100644 --- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs +++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs @@ -1411,7 +1411,7 @@ public static class OpenIddictExtensions foreach (var element in value.EnumerateArray()) { - var item = element.GetString()!; + var item = element.ToString()!; if (set.Add(item)) { identity.AddClaim(new Claim( diff --git a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs index db47f602..124ef251 100644 --- a/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs @@ -2312,7 +2312,10 @@ public class OpenIddictExtensionsTests var identity = new ClaimsIdentity(); // Act - identity.AddClaims("type", JsonSerializer.Deserialize(@"[""Fabrikam"",""Contoso""]"), "issuer"); + identity.AddClaims( + "type", + JsonSerializer.Deserialize(@"[""Fabrikam"", ""Contoso"", { ""Foo"": ""Bar"" }]"), + "issuer"); // Assert var claims = identity.FindAll("type").ToArray(); @@ -2322,6 +2325,9 @@ public class OpenIddictExtensionsTests Assert.Equal("Contoso", claims[1].Value); Assert.Equal(ClaimValueTypes.String, claims[1].ValueType); Assert.Equal("issuer", claims[1].Issuer); + Assert.Equal("{ \"Foo\": \"Bar\" }", claims[2].Value); + Assert.Equal("JSON", claims[2].ValueType); + Assert.Equal("issuer", claims[2].Issuer); } [Fact] @@ -2331,17 +2337,23 @@ public class OpenIddictExtensionsTests var principal = new ClaimsPrincipal(new ClaimsIdentity()); // Act - principal.AddClaims("type", JsonSerializer.Deserialize(@"[""Fabrikam"",""Contoso""]"), "issuer"); + principal.AddClaims( + "type", + JsonSerializer.Deserialize(@"[""Fabrikam"", ""Contoso"", { ""Foo"": ""Bar"" }]"), + "issuer"); // Assert var claims = principal.FindAll("type").ToArray(); - Assert.Equal(2, claims.Length); + Assert.Equal(3, claims.Length); Assert.Equal("Fabrikam", claims[0].Value); Assert.Equal(ClaimValueTypes.String, claims[0].ValueType); Assert.Equal("issuer", claims[0].Issuer); Assert.Equal("Contoso", claims[1].Value); Assert.Equal(ClaimValueTypes.String, claims[1].ValueType); Assert.Equal("issuer", claims[1].Issuer); + Assert.Equal("{ \"Foo\": \"Bar\" }", claims[2].Value); + Assert.Equal("JSON", claims[2].ValueType); + Assert.Equal("issuer", claims[2].Issuer); } [Fact] From a1a0815cffe021031511eb721a4426f296cfbca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Tue, 13 Dec 2022 12:31:29 +0100 Subject: [PATCH 07/10] Add XML documentation to the dispatcher/factory/handler filter classes --- ...OpenIddictClientWebIntegrationGenerator.cs | 11 +---- ...OpenIddictClientAspNetCoreConfiguration.cs | 12 ++---- ...penIddictClientAspNetCoreHandlerFilters.cs | 6 +++ ...IddictClientDataProtectionConfiguration.cs | 8 +--- ...ddictClientDataProtectionHandlerFilters.cs | 1 + .../OpenIddictClientOwinConfiguration.cs | 2 + .../OpenIddictClientOwinHandlerFilters.cs | 5 +++ ...nIddictClientSystemNetHttpConfiguration.cs | 7 ++++ ...IddictClientSystemNetHttpHandlerFilters.cs | 1 + ...IddictClientWebIntegrationConfiguration.cs | 6 +-- .../IOpenIddictClientDispatcher.cs | 12 ++++++ .../IOpenIddictClientFactory.cs | 11 +++++ .../IOpenIddictClientHandlerFilter.cs | 14 +++++++ .../OpenIddictClientConfiguration.cs | 11 +++-- .../OpenIddictClientDispatcher.cs | 4 ++ .../OpenIddictClientFactory.cs | 4 ++ .../OpenIddictClientHandlerFilters.cs | 25 +++++++++++ ...OpenIddictServerAspNetCoreConfiguration.cs | 19 ++------- ...penIddictServerAspNetCoreHandlerFilters.cs | 11 +++++ ...IddictServerDataProtectionConfiguration.cs | 8 +--- ...ddictServerDataProtectionHandlerFilters.cs | 1 + .../OpenIddictServerOwinConfiguration.cs | 2 + .../OpenIddictServerOwinHandlerFilters.cs | 10 +++++ .../IOpenIddictServerDispatcher.cs | 12 ++++++ .../IOpenIddictServerFactory.cs | 11 +++++ .../IOpenIddictServerHandlerFilter.cs | 14 +++++++ .../OpenIddictServerConfiguration.cs | 7 +--- .../OpenIddictServerDispatcher.cs | 4 ++ .../OpenIddictServerFactory.cs | 4 ++ .../OpenIddictServerHandlerFilters.cs | 42 +++++++++++++++++++ ...IddictValidationAspNetCoreConfiguration.cs | 12 ++---- ...ddictValidationAspNetCoreHandlerFilters.cs | 1 + ...ctValidationDataProtectionConfiguration.cs | 8 +--- .../OpenIddictValidationOwinConfiguration.cs | 1 + .../OpenIddictValidationOwinHandlerFilters.cs | 1 + ...alidationServerIntegrationConfiguration.cs | 13 +----- ...ictValidationSystemNetHttpConfiguration.cs | 9 +++- ...ctValidationSystemNetHttpHandlerFilters.cs | 1 + .../IOpenIddictValidationDispatcher.cs | 12 ++++++ .../IOpenIddictValidationFactory.cs | 11 +++++ .../IOpenIddictValidationHandlerFilter.cs | 15 ++++++- .../OpenIddictValidationConfiguration.cs | 11 +++-- .../OpenIddictValidationDispatcher.cs | 4 ++ .../OpenIddictValidationFactory.cs | 4 ++ .../OpenIddictValidationHandlerFilters.cs | 8 ++++ 45 files changed, 300 insertions(+), 96 deletions(-) diff --git a/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs b/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs index 32028c4d..8d7e8a6b 100644 --- a/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs +++ b/gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs @@ -436,11 +436,7 @@ public sealed partial class OpenIddictClientWebIntegrationConfiguration public {{ provider.name }}(IServiceProvider provider) => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); - /// - /// Ensures the {{ provider.name }} configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictClientWebIntegrationOptions.{{ provider.name }} options) { {{~ for setting in provider.settings ~}} @@ -515,10 +511,7 @@ public sealed partial class OpenIddictClientWebIntegrationConfiguration {{~ end ~}} } - /// - /// Registers the {{ provider.name }} integration in the OpenIddict client options. - /// - /// The options instance to initialize. + /// public void Configure(OpenIddictClientOptions options) { // Resolve the provider options from the service provider and create a registration based on the specified settings. diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConfiguration.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConfiguration.cs index 2cd0b55c..f8d4bc5d 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConfiguration.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreConfiguration.cs @@ -17,10 +17,7 @@ public sealed class OpenIddictClientAspNetCoreConfiguration : IConfigureOptions< IConfigureOptions, IPostConfigureOptions { - /// - /// Registers the OpenIddict client handler in the global authentication options. - /// - /// The options instance to initialize. + /// public void Configure(AuthenticationOptions options) { if (options is null) @@ -39,6 +36,7 @@ public sealed class OpenIddictClientAspNetCoreConfiguration : IConfigureOptions< OpenIddictClientAspNetCoreDefaults.AuthenticationScheme, displayName: null); } + /// public void Configure(OpenIddictClientOptions options) { if (options is null) @@ -50,11 +48,7 @@ public sealed class OpenIddictClientAspNetCoreConfiguration : IConfigureOptions< options.Handlers.AddRange(OpenIddictClientAspNetCoreHandlers.DefaultHandlers); } - /// - /// Ensures that the authentication configuration is in a consistent and valid state. - /// - /// The authentication scheme associated with the handler instance. - /// The options instance to initialize. + /// public void PostConfigure(string? name, AuthenticationOptions options) { if (options is null) diff --git a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlerFilters.cs b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlerFilters.cs index acaa53fa..4b7da143 100644 --- a/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlerFilters.cs +++ b/src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandlerFilters.cs @@ -26,6 +26,7 @@ public static class OpenIddictClientAspNetCoreHandlerFilters public RequireErrorPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -42,6 +43,7 @@ public static class OpenIddictClientAspNetCoreHandlerFilters /// public sealed class RequireHttpRequest : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -64,6 +66,7 @@ public static class OpenIddictClientAspNetCoreHandlerFilters public RequirePostLogoutRedirectionEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -86,6 +89,7 @@ public static class OpenIddictClientAspNetCoreHandlerFilters public RequireRedirectionEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -107,6 +111,7 @@ public static class OpenIddictClientAspNetCoreHandlerFilters public RequireStatusCodePagesIntegrationEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -128,6 +133,7 @@ public static class OpenIddictClientAspNetCoreHandlerFilters public RequireTransportSecurityRequirementEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionConfiguration.cs b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionConfiguration.cs index e41a1eae..2da06dd5 100644 --- a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionConfiguration.cs +++ b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionConfiguration.cs @@ -26,6 +26,7 @@ public sealed class OpenIddictClientDataProtectionConfiguration : IConfigureOpti public OpenIddictClientDataProtectionConfiguration(IDataProtectionProvider dataProtectionProvider) => _dataProtectionProvider = dataProtectionProvider; + /// public void Configure(OpenIddictClientOptions options) { if (options is null) @@ -37,12 +38,7 @@ public sealed class OpenIddictClientDataProtectionConfiguration : IConfigureOpti options.Handlers.AddRange(OpenIddictClientDataProtectionHandlers.DefaultHandlers); } - /// - /// Populates the default OpenIddict ASP.NET Core Data Protection server options - /// and ensures that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictClientDataProtectionOptions options) { if (options is null) diff --git a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlerFilters.cs b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlerFilters.cs index 63b4331b..eb965363 100644 --- a/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlerFilters.cs +++ b/src/OpenIddict.Client.DataProtection/OpenIddictClientDataProtectionHandlerFilters.cs @@ -17,6 +17,7 @@ public static class OpenIddictClientDataProtectionHandlerFilters /// public sealed class RequireDataProtectionTokenFormat : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs index 40147741..bebf1c8d 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinConfiguration.cs @@ -16,6 +16,7 @@ namespace OpenIddict.Client.Owin; public sealed class OpenIddictClientOwinConfiguration : IConfigureOptions, IPostConfigureOptions { + /// public void Configure(OpenIddictClientOptions options) { if (options is null) @@ -27,6 +28,7 @@ public sealed class OpenIddictClientOwinConfiguration : IConfigureOptions public void PostConfigure(string? name, OpenIddictClientOwinOptions options) { if (options is null) diff --git a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlerFilters.cs b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlerFilters.cs index df79287c..4a494c6b 100644 --- a/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlerFilters.cs +++ b/src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlerFilters.cs @@ -27,6 +27,7 @@ public static class OpenIddictClientOwinHandlerFilters public RequirePostLogoutRedirectionEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -49,6 +50,7 @@ public static class OpenIddictClientOwinHandlerFilters public RequireRedirectionEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -70,6 +72,7 @@ public static class OpenIddictClientOwinHandlerFilters public RequireErrorPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -86,6 +89,7 @@ public static class OpenIddictClientOwinHandlerFilters /// public sealed class RequireOwinRequest : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -107,6 +111,7 @@ public static class OpenIddictClientOwinHandlerFilters public RequireTransportSecurityRequirementEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs index d83a8bc1..f8302944 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs @@ -21,10 +21,15 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio #if !SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER private readonly IServiceProvider _provider; + /// + /// Creates a new instance of the class. + /// + /// The service provider. public OpenIddictClientSystemNetHttpConfiguration(IServiceProvider provider) => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); #endif + /// public void Configure(OpenIddictClientOptions options) { if (options is null) @@ -36,8 +41,10 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio options.Handlers.AddRange(OpenIddictClientSystemNetHttpHandlers.DefaultHandlers); } + /// public void Configure(HttpClientFactoryOptions options) => Configure(Options.DefaultName, options); + /// public void Configure(string? name, HttpClientFactoryOptions options) { if (options is null) diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlerFilters.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlerFilters.cs index 43028296..1dc74db3 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlerFilters.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlerFilters.cs @@ -16,6 +16,7 @@ public static class OpenIddictClientSystemNetHttpHandlerFilters /// public sealed class RequireHttpMetadataUri : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(BaseExternalContext context) { if (context is null) diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationConfiguration.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationConfiguration.cs index fb1140d1..b1ee934b 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationConfiguration.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationConfiguration.cs @@ -15,11 +15,7 @@ namespace OpenIddict.Client.WebIntegration; [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed partial class OpenIddictClientWebIntegrationConfiguration : IConfigureOptions { - /// - /// Populates the default OpenIddict client Web integration options - /// and ensures that the configuration is in a consistent and valid state. - /// - /// The options instance to initialize. + /// public void Configure(OpenIddictClientOptions options) { if (options is null) diff --git a/src/OpenIddict.Client/IOpenIddictClientDispatcher.cs b/src/OpenIddict.Client/IOpenIddictClientDispatcher.cs index b1a71cc7..385f30cd 100644 --- a/src/OpenIddict.Client/IOpenIddictClientDispatcher.cs +++ b/src/OpenIddict.Client/IOpenIddictClientDispatcher.cs @@ -8,8 +8,20 @@ using System.ComponentModel; namespace OpenIddict.Client; +/// +/// Represents a service able to dispatch events to a list of handlers. +/// [EditorBrowsable(EditorBrowsableState.Advanced)] public interface IOpenIddictClientDispatcher { + /// + /// Dispatches an event of the specified type to the handlers that + /// implement . + /// + /// The type of the context associated with the event to dispatch. + /// The context associated with the event to dispatch. + /// + /// A that can be used to monitor the asynchronous operation. + /// ValueTask DispatchAsync(TContext context) where TContext : BaseContext; } diff --git a/src/OpenIddict.Client/IOpenIddictClientFactory.cs b/src/OpenIddict.Client/IOpenIddictClientFactory.cs index 88df0a0a..24e1dfd2 100644 --- a/src/OpenIddict.Client/IOpenIddictClientFactory.cs +++ b/src/OpenIddict.Client/IOpenIddictClientFactory.cs @@ -8,8 +8,19 @@ using System.ComponentModel; namespace OpenIddict.Client; +/// +/// Represents a service responsible for creating transactions. +/// [EditorBrowsable(EditorBrowsableState.Never)] public interface IOpenIddictClientFactory { + /// + /// Creates a new that is used as a + /// way to store per-request data needed to process the requested operation. + /// + /// + /// A that can be used to monitor the asynchronous + /// operation, whose result returns the created transaction. + /// ValueTask CreateTransactionAsync(); } diff --git a/src/OpenIddict.Client/IOpenIddictClientHandlerFilter.cs b/src/OpenIddict.Client/IOpenIddictClientHandlerFilter.cs index 2277a641..f11b828f 100644 --- a/src/OpenIddict.Client/IOpenIddictClientHandlerFilter.cs +++ b/src/OpenIddict.Client/IOpenIddictClientHandlerFilter.cs @@ -6,7 +6,21 @@ namespace OpenIddict.Client; +/// +/// Represents a handler filter responsible for determining whether a +/// handler should process an event depending on the specified context. +/// +/// The type of the context associated with events filtered by this instance. public interface IOpenIddictClientHandlerFilter where TContext : BaseContext { + /// + /// Determines whether the handler referencing this filter instance should + /// be instantiated and process the event, based on the specified context. + /// + /// The context associated with the event to process. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose boolean result determines whether the handler will be invoked or not. + /// ValueTask IsActiveAsync(TContext context); } diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 98a6788d..bdb34c68 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -21,15 +21,14 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions + /// Creates a new instance of the class. + /// + /// The OpenIddict client service. public OpenIddictClientConfiguration(OpenIddictClientService service) => _service = service ?? throw new ArgumentNullException(nameof(service)); - /// - /// Populates the default OpenIddict client options and ensures - /// that the configuration is in a consistent and valid state. - /// - /// The authentication scheme associated with the handler instance. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictClientOptions options) { if (options is null) diff --git a/src/OpenIddict.Client/OpenIddictClientDispatcher.cs b/src/OpenIddict.Client/OpenIddictClientDispatcher.cs index 91a6c59d..4b3809d5 100644 --- a/src/OpenIddict.Client/OpenIddictClientDispatcher.cs +++ b/src/OpenIddict.Client/OpenIddictClientDispatcher.cs @@ -10,6 +10,9 @@ using Microsoft.Extensions.Options; namespace OpenIddict.Client; +/// +/// Represents a service able to dispatch events to a list of handlers. +/// [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictClientDispatcher : IOpenIddictClientDispatcher { @@ -30,6 +33,7 @@ public sealed class OpenIddictClientDispatcher : IOpenIddictClientDispatcher _provider = provider ?? throw new ArgumentNullException(nameof(provider)); } + /// public async ValueTask DispatchAsync(TContext context) where TContext : BaseContext { if (context is null) diff --git a/src/OpenIddict.Client/OpenIddictClientFactory.cs b/src/OpenIddict.Client/OpenIddictClientFactory.cs index 552f4375..59cf992d 100644 --- a/src/OpenIddict.Client/OpenIddictClientFactory.cs +++ b/src/OpenIddict.Client/OpenIddictClientFactory.cs @@ -10,6 +10,9 @@ using Microsoft.Extensions.Options; namespace OpenIddict.Client; +/// +/// Represents a service responsible for creating transactions. +/// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class OpenIddictClientFactory : IOpenIddictClientFactory { @@ -27,6 +30,7 @@ public sealed class OpenIddictClientFactory : IOpenIddictClientFactory _options = options ?? throw new ArgumentNullException(nameof(options)); } + /// public ValueTask CreateTransactionAsync() => new(new OpenIddictClientTransaction { diff --git a/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs b/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs index d8e907e4..8bf0de5d 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs @@ -16,6 +16,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireAuthorizationCodeValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -32,6 +33,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireBackchannelAccessTokenValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -48,6 +50,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireBackchannelIdentityTokenPrincipal : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -64,6 +67,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireBackchannelIdentityTokenValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -80,6 +84,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireClientAssertionTokenGenerated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -96,6 +101,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireFrontchannelAccessTokenValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -112,6 +118,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireFrontchannelIdentityTokenPrincipal : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -128,6 +135,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireFrontchannelIdentityTokenValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -145,6 +153,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireInteractiveGrantType : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessChallengeContext context) { if (context is null) @@ -166,6 +175,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireJsonWebTokenFormat : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) @@ -182,6 +192,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireLoginStateTokenGenerated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessChallengeContext context) { if (context is null) @@ -198,6 +209,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireLogoutStateTokenGenerated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignOutContext context) { if (context is null) @@ -214,6 +226,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequirePostLogoutRedirectionRequest : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -230,6 +243,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireRedirectionRequest : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -246,6 +260,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireRefreshTokenValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -262,6 +277,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireStateTokenPrincipal : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -278,6 +294,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireStateTokenValidated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -294,6 +311,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireTokenEntryCreated : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) @@ -310,6 +328,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireTokenIdResolved : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ValidateTokenContext context) { if (context is null) @@ -326,6 +345,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireTokenPayloadPersisted : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) @@ -342,6 +362,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireTokenRequest : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -358,6 +379,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireTokenStorageEnabled : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -374,6 +396,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireUserinfoRequest : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -390,6 +413,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireUserinfoTokenExtracted : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -406,6 +430,7 @@ public static class OpenIddictClientHandlerFilters /// public sealed class RequireUserinfoTokenPrincipal : IOpenIddictClientHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConfiguration.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConfiguration.cs index c6681d64..fd81da00 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConfiguration.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreConfiguration.cs @@ -18,10 +18,7 @@ public sealed class OpenIddictServerAspNetCoreConfiguration : IConfigureOptions< IPostConfigureOptions, IPostConfigureOptions { - /// - /// Registers the OpenIddict server handler in the global authentication options. - /// - /// The options instance to initialize. + /// public void Configure(AuthenticationOptions options) { if (options is null) @@ -40,6 +37,7 @@ public sealed class OpenIddictServerAspNetCoreConfiguration : IConfigureOptions< OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, displayName: null); } + /// public void Configure(OpenIddictServerOptions options) { if (options is null) @@ -51,11 +49,7 @@ public sealed class OpenIddictServerAspNetCoreConfiguration : IConfigureOptions< options.Handlers.AddRange(OpenIddictServerAspNetCoreHandlers.DefaultHandlers); } - /// - /// Ensures that the authentication configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, AuthenticationOptions options) { if (options is null) @@ -106,12 +100,7 @@ public sealed class OpenIddictServerAspNetCoreConfiguration : IConfigureOptions< } } - /// - /// Populates the default OpenIddict server ASP.NET Core options and - /// ensures that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictServerAspNetCoreOptions options) { if (options is null) diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlerFilters.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlerFilters.cs index 4934091f..86cd5e98 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlerFilters.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlerFilters.cs @@ -26,6 +26,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireAuthorizationRequestCachingEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -48,6 +49,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireAuthorizationEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -69,6 +71,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireErrorPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -85,6 +88,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters /// public sealed class RequireHttpRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -106,6 +110,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireLogoutRequestCachingEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -128,6 +133,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireLogoutEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -149,6 +155,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireStatusCodePagesIntegrationEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -170,6 +177,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireTransportSecurityRequirementEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -192,6 +200,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireTokenEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -214,6 +223,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireUserinfoEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -236,6 +246,7 @@ public static class OpenIddictServerAspNetCoreHandlerFilters public RequireVerificationEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConfiguration.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConfiguration.cs index de8b378a..2b8f1830 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConfiguration.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConfiguration.cs @@ -26,6 +26,7 @@ public sealed class OpenIddictServerDataProtectionConfiguration : IConfigureOpti public OpenIddictServerDataProtectionConfiguration(IDataProtectionProvider dataProtectionProvider) => _dataProtectionProvider = dataProtectionProvider; + /// public void Configure(OpenIddictServerOptions options) { if (options is null) @@ -37,12 +38,7 @@ public sealed class OpenIddictServerDataProtectionConfiguration : IConfigureOpti options.Handlers.AddRange(OpenIddictServerDataProtectionHandlers.DefaultHandlers); } - /// - /// Populates the default OpenIddict ASP.NET Core Data Protection server options - /// and ensures that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictServerDataProtectionOptions options) { if (options is null) diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlerFilters.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlerFilters.cs index ec627106..12db3e80 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlerFilters.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlerFilters.cs @@ -17,6 +17,7 @@ public static class OpenIddictServerDataProtectionHandlerFilters /// public sealed class RequireDataProtectionTokenFormat : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs index caf5a9a0..c3498696 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinConfiguration.cs @@ -16,6 +16,7 @@ namespace OpenIddict.Server.Owin; public sealed class OpenIddictServerOwinConfiguration : IConfigureOptions, IPostConfigureOptions { + /// public void Configure(OpenIddictServerOptions options) { if (options is null) @@ -27,6 +28,7 @@ public sealed class OpenIddictServerOwinConfiguration : IConfigureOptions public void PostConfigure(string? name, OpenIddictServerOwinOptions options) { if (options is null) diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlerFilters.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlerFilters.cs index e2e5cedc..d8799f15 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlerFilters.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlerFilters.cs @@ -24,6 +24,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireAuthorizationRequestCachingEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -46,6 +47,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireAuthorizationEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -67,6 +69,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireErrorPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -88,6 +91,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireLogoutRequestCachingEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -110,6 +114,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireLogoutEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -126,6 +131,7 @@ public static class OpenIddictServerOwinHandlerFilters /// public sealed class RequireOwinRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -147,6 +153,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireTransportSecurityRequirementEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -169,6 +176,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireTokenEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -191,6 +199,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireUserinfoEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -213,6 +222,7 @@ public static class OpenIddictServerOwinHandlerFilters public RequireVerificationEndpointPassthroughEnabled(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Server/IOpenIddictServerDispatcher.cs b/src/OpenIddict.Server/IOpenIddictServerDispatcher.cs index d6d7ec12..22002fe9 100644 --- a/src/OpenIddict.Server/IOpenIddictServerDispatcher.cs +++ b/src/OpenIddict.Server/IOpenIddictServerDispatcher.cs @@ -8,8 +8,20 @@ using System.ComponentModel; namespace OpenIddict.Server; +/// +/// Represents a service able to dispatch events to a list of handlers. +/// [EditorBrowsable(EditorBrowsableState.Advanced)] public interface IOpenIddictServerDispatcher { + /// + /// Dispatches an event of the specified type to the handlers that + /// implement . + /// + /// The type of the context associated with the event to dispatch. + /// The context associated with the event to dispatch. + /// + /// A that can be used to monitor the asynchronous operation. + /// ValueTask DispatchAsync(TContext context) where TContext : BaseContext; } diff --git a/src/OpenIddict.Server/IOpenIddictServerFactory.cs b/src/OpenIddict.Server/IOpenIddictServerFactory.cs index 8d3d136c..40fb02d3 100644 --- a/src/OpenIddict.Server/IOpenIddictServerFactory.cs +++ b/src/OpenIddict.Server/IOpenIddictServerFactory.cs @@ -8,8 +8,19 @@ using System.ComponentModel; namespace OpenIddict.Server; +/// +/// Represents a service responsible for creating transactions. +/// [EditorBrowsable(EditorBrowsableState.Never)] public interface IOpenIddictServerFactory { + /// + /// Creates a new that is used as a + /// way to store per-request data needed to process the requested operation. + /// + /// + /// A that can be used to monitor the asynchronous + /// operation, whose result returns the created transaction. + /// ValueTask CreateTransactionAsync(); } diff --git a/src/OpenIddict.Server/IOpenIddictServerHandlerFilter.cs b/src/OpenIddict.Server/IOpenIddictServerHandlerFilter.cs index 60154590..fd59118a 100644 --- a/src/OpenIddict.Server/IOpenIddictServerHandlerFilter.cs +++ b/src/OpenIddict.Server/IOpenIddictServerHandlerFilter.cs @@ -6,7 +6,21 @@ namespace OpenIddict.Server; +/// +/// Represents a handler filter responsible for determining whether a +/// handler should process an event depending on the specified context. +/// +/// The type of the context associated with events filtered by this instance. public interface IOpenIddictServerHandlerFilter where TContext : BaseContext { + /// + /// Determines whether the handler referencing this filter instance should + /// be instantiated and process the event, based on the specified context. + /// + /// The context associated with the event to process. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose boolean result determines whether the handler will be invoked or not. + /// ValueTask IsActiveAsync(TContext context); } diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs index 1d8d7327..76e12fd4 100644 --- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs +++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs @@ -18,12 +18,7 @@ namespace OpenIddict.Server; [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictServerConfiguration : IPostConfigureOptions { - /// - /// Populates the default OpenIddict server options and ensures - /// that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictServerOptions options) { if (options is null) diff --git a/src/OpenIddict.Server/OpenIddictServerDispatcher.cs b/src/OpenIddict.Server/OpenIddictServerDispatcher.cs index 7618f66d..aafa17ea 100644 --- a/src/OpenIddict.Server/OpenIddictServerDispatcher.cs +++ b/src/OpenIddict.Server/OpenIddictServerDispatcher.cs @@ -10,6 +10,9 @@ using Microsoft.Extensions.Options; namespace OpenIddict.Server; +/// +/// Represents a service able to dispatch events to a list of handlers. +/// [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictServerDispatcher : IOpenIddictServerDispatcher { @@ -30,6 +33,7 @@ public sealed class OpenIddictServerDispatcher : IOpenIddictServerDispatcher _provider = provider ?? throw new ArgumentNullException(nameof(provider)); } + /// public async ValueTask DispatchAsync(TContext context) where TContext : BaseContext { if (context is null) diff --git a/src/OpenIddict.Server/OpenIddictServerFactory.cs b/src/OpenIddict.Server/OpenIddictServerFactory.cs index 7e583712..df714124 100644 --- a/src/OpenIddict.Server/OpenIddictServerFactory.cs +++ b/src/OpenIddict.Server/OpenIddictServerFactory.cs @@ -10,6 +10,9 @@ using Microsoft.Extensions.Options; namespace OpenIddict.Server; +/// +/// Represents a service responsible for creating transactions. +/// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class OpenIddictServerFactory : IOpenIddictServerFactory { @@ -27,6 +30,7 @@ public sealed class OpenIddictServerFactory : IOpenIddictServerFactory _options = options ?? throw new ArgumentNullException(nameof(options)); } + /// public ValueTask CreateTransactionAsync() => new(new OpenIddictServerTransaction { diff --git a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs index f905a1c7..800419cd 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs @@ -16,6 +16,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAccessTokenGenerated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignInContext context) { if (context is null) @@ -32,6 +33,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAccessTokenValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -48,6 +50,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAuthorizationCodeGenerated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignInContext context) { if (context is null) @@ -64,6 +67,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAuthorizationCodeValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -80,6 +84,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAuthorizationIdResolved : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ValidateTokenContext context) { if (context is null) @@ -96,6 +101,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAuthorizationRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -112,6 +118,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireAuthorizationStorageEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -128,6 +135,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireClientIdParameter : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -144,6 +152,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireConfigurationRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -160,6 +169,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireCryptographyRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -176,6 +186,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireDegradedModeDisabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -192,6 +203,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireDeviceCodeGenerated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignInContext context) { if (context is null) @@ -208,6 +220,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireDeviceCodeValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -224,6 +237,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireDeviceRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -240,6 +254,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireEndpointPermissionsEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -256,6 +271,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireGenericTokenValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -272,6 +288,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireGrantTypePermissionsEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -288,6 +305,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireIdentityTokenGenerated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignInContext context) { if (context is null) @@ -304,6 +322,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireIdentityTokenValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -320,6 +339,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireIntrospectionRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -336,6 +356,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireJsonWebTokenFormat : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) @@ -352,6 +373,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireLogoutRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -368,6 +390,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequirePostLogoutRedirectUriParameter : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -384,6 +407,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireReferenceAccessTokensEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -400,6 +424,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireReferenceRefreshTokensEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -416,6 +441,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireRefreshTokenGenerated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignInContext context) { if (context is null) @@ -432,6 +458,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireRefreshTokenValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -448,6 +475,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireResponseTypePermissionsEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -464,6 +492,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireRevocationRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -480,6 +509,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireScopePermissionsEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -496,6 +526,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireScopeValidationEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -512,6 +543,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireSlidingRefreshTokenExpirationEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -528,6 +560,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireTokenIdResolved : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ValidateTokenContext context) { if (context is null) @@ -544,6 +577,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireTokenEntryCreated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) @@ -560,6 +594,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireTokenLifetimeValidationEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ValidateTokenContext context) { if (context is null) @@ -576,6 +611,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireTokenPayloadPersisted : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(GenerateTokenContext context) { if (context is null) @@ -592,6 +628,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireTokenRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -608,6 +645,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireTokenStorageEnabled : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -624,6 +662,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireUserCodeGenerated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessSignInContext context) { if (context is null) @@ -640,6 +679,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireUserCodeValidated : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -656,6 +696,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireUserinfoRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -672,6 +713,7 @@ public static class OpenIddictServerHandlerFilters /// public sealed class RequireVerificationRequest : IOpenIddictServerHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreConfiguration.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreConfiguration.cs index 373801af..ba3fc4c7 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreConfiguration.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreConfiguration.cs @@ -17,10 +17,7 @@ public sealed class OpenIddictValidationAspNetCoreConfiguration : IConfigureOpti IConfigureOptions, IPostConfigureOptions { - /// - /// Registers the OpenIddict validation handler in the global authentication options. - /// - /// The options instance to initialize. + /// public void Configure(AuthenticationOptions options) { if (options is null) @@ -39,6 +36,7 @@ public sealed class OpenIddictValidationAspNetCoreConfiguration : IConfigureOpti OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme, displayName: null); } + /// public void Configure(OpenIddictValidationOptions options) { if (options is null) @@ -50,11 +48,7 @@ public sealed class OpenIddictValidationAspNetCoreConfiguration : IConfigureOpti options.Handlers.AddRange(OpenIddictValidationAspNetCoreHandlers.DefaultHandlers); } - /// - /// Ensures that the authentication configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, AuthenticationOptions options) { if (options is null) diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlerFilters.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlerFilters.cs index d95708ed..906d7c6e 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlerFilters.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlerFilters.cs @@ -20,6 +20,7 @@ public static class OpenIddictValidationAspNetCoreHandlerFilters /// public sealed class RequireHttpRequest : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionConfiguration.cs b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionConfiguration.cs index 878eebf7..09c28cdc 100644 --- a/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionConfiguration.cs +++ b/src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionConfiguration.cs @@ -26,6 +26,7 @@ public sealed class OpenIddictValidationDataProtectionConfiguration : IConfigure public OpenIddictValidationDataProtectionConfiguration(IDataProtectionProvider dataProtectionProvider) => _dataProtectionProvider = dataProtectionProvider; + /// public void Configure(OpenIddictValidationOptions options) { if (options is null) @@ -37,12 +38,7 @@ public sealed class OpenIddictValidationDataProtectionConfiguration : IConfigure options.Handlers.AddRange(OpenIddictValidationDataProtectionHandlers.DefaultHandlers); } - /// - /// Populates the default OpenIddict ASP.NET Core Data Protection validation options - /// and ensures that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictValidationDataProtectionOptions options) { if (options is null) diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinConfiguration.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinConfiguration.cs index 28c72d7d..e2c7365a 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinConfiguration.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinConfiguration.cs @@ -15,6 +15,7 @@ namespace OpenIddict.Validation.Owin; [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictValidationOwinConfiguration : IConfigureOptions { + /// public void Configure(OpenIddictValidationOptions options) { if (options is null) diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlerFilters.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlerFilters.cs index 700c3163..c1e89144 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlerFilters.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlerFilters.cs @@ -18,6 +18,7 @@ public static class OpenIddictValidationOwinHandlerFilters /// public sealed class RequireOwinRequest : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) diff --git a/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs b/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs index d1a8bf0b..7f7c4139 100644 --- a/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs +++ b/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs @@ -26,11 +26,7 @@ public sealed class OpenIddictValidationServerIntegrationConfiguration : IConfig public OpenIddictValidationServerIntegrationConfiguration(IOptionsMonitor options) => _options = options ?? throw new ArgumentNullException(nameof(options)); - /// - /// Populates the default OpenIddict validation/server integration options - /// and ensures that the configuration is in a consistent and valid state. - /// - /// The options instance to initialize. + /// public void Configure(OpenIddictValidationOptions options) { if (options is null) @@ -58,12 +54,7 @@ public sealed class OpenIddictValidationServerIntegrationConfiguration : IConfig options.EnableTokenEntryValidation = _options.CurrentValue.UseReferenceAccessTokens; } - /// - /// Populates the default OpenIddict validation/server integration options - /// and ensures that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictValidationOptions options) { if (options is null) diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs index eb7360de..09428463 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs @@ -20,11 +20,16 @@ public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureO { #if !SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER private readonly IServiceProvider _provider; - + + /// + /// Creates a new instance of the class. + /// + /// The service provider. public OpenIddictValidationSystemNetHttpConfiguration(IServiceProvider provider) => _provider = provider ?? throw new ArgumentNullException(nameof(provider)); #endif + /// public void Configure(OpenIddictValidationOptions options) { if (options is null) @@ -36,8 +41,10 @@ public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureO options.Handlers.AddRange(OpenIddictValidationSystemNetHttpHandlers.DefaultHandlers); } + /// public void Configure(HttpClientFactoryOptions options) => Configure(Options.DefaultName, options); + /// public void Configure(string? name, HttpClientFactoryOptions options) { if (options is null) diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlerFilters.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlerFilters.cs index 29f11ec3..e7ded20d 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlerFilters.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlerFilters.cs @@ -16,6 +16,7 @@ public static class OpenIddictValidationSystemNetHttpHandlerFilters /// public sealed class RequireHttpMetadataUri : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseExternalContext context) { if (context is null) diff --git a/src/OpenIddict.Validation/IOpenIddictValidationDispatcher.cs b/src/OpenIddict.Validation/IOpenIddictValidationDispatcher.cs index f47226aa..694623bf 100644 --- a/src/OpenIddict.Validation/IOpenIddictValidationDispatcher.cs +++ b/src/OpenIddict.Validation/IOpenIddictValidationDispatcher.cs @@ -8,8 +8,20 @@ using System.ComponentModel; namespace OpenIddict.Validation; +/// +/// Represents a service able to dispatch events to a list of handlers. +/// [EditorBrowsable(EditorBrowsableState.Advanced)] public interface IOpenIddictValidationDispatcher { + /// + /// Dispatches an event of the specified type to the handlers that + /// implement . + /// + /// The type of the context associated with the event to dispatch. + /// The context associated with the event to dispatch. + /// + /// A that can be used to monitor the asynchronous operation. + /// ValueTask DispatchAsync(TContext context) where TContext : BaseContext; } diff --git a/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs b/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs index 8976364f..d88c5d26 100644 --- a/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs +++ b/src/OpenIddict.Validation/IOpenIddictValidationFactory.cs @@ -8,8 +8,19 @@ using System.ComponentModel; namespace OpenIddict.Validation; +/// +/// Represents a service responsible for creating transactions. +/// [EditorBrowsable(EditorBrowsableState.Never)] public interface IOpenIddictValidationFactory { + /// + /// Creates a new that is used as a + /// way to store per-request data needed to process the requested operation. + /// + /// + /// A that can be used to monitor the asynchronous + /// operation, whose result returns the created transaction. + /// ValueTask CreateTransactionAsync(); } diff --git a/src/OpenIddict.Validation/IOpenIddictValidationHandlerFilter.cs b/src/OpenIddict.Validation/IOpenIddictValidationHandlerFilter.cs index e46cba00..19f3f0df 100644 --- a/src/OpenIddict.Validation/IOpenIddictValidationHandlerFilter.cs +++ b/src/OpenIddict.Validation/IOpenIddictValidationHandlerFilter.cs @@ -5,8 +5,21 @@ */ namespace OpenIddict.Validation; - +/// +/// Represents a handler filter responsible for determining whether a +/// handler should process an event depending on the specified context. +/// +/// The type of the context associated with events filtered by this instance. public interface IOpenIddictValidationHandlerFilter where TContext : BaseContext { + /// + /// Determines whether the handler referencing this filter instance should + /// be instantiated and process the event, based on the specified context. + /// + /// The context associated with the event to process. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose boolean result determines whether the handler will be invoked or not. + /// ValueTask IsActiveAsync(TContext context); } diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index ebaa60ad..66575995 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -18,15 +18,14 @@ public sealed class OpenIddictValidationConfiguration : IPostConfigureOptions + /// Creates a new instance of the class. + /// + /// The validation service. public OpenIddictValidationConfiguration(OpenIddictValidationService service) => _service = service ?? throw new ArgumentNullException(nameof(service)); - /// - /// Populates the default OpenIddict validation options and ensures - /// that the configuration is in a consistent and valid state. - /// - /// The name of the options instance to configure, if applicable. - /// The options instance to initialize. + /// public void PostConfigure(string? name, OpenIddictValidationOptions options) { if (options is null) diff --git a/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs b/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs index b1fd7963..be57f51c 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationDispatcher.cs @@ -10,6 +10,9 @@ using Microsoft.Extensions.Options; namespace OpenIddict.Validation; +/// +/// Represents a service able to dispatch events to a list of handlers. +/// [EditorBrowsable(EditorBrowsableState.Advanced)] public sealed class OpenIddictValidationDispatcher : IOpenIddictValidationDispatcher { @@ -30,6 +33,7 @@ public sealed class OpenIddictValidationDispatcher : IOpenIddictValidationDispat _provider = provider ?? throw new ArgumentNullException(nameof(provider)); } + /// public async ValueTask DispatchAsync(TContext context) where TContext : BaseContext { if (context is null) diff --git a/src/OpenIddict.Validation/OpenIddictValidationFactory.cs b/src/OpenIddict.Validation/OpenIddictValidationFactory.cs index 03ac8102..85e836c9 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationFactory.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationFactory.cs @@ -10,6 +10,9 @@ using Microsoft.Extensions.Options; namespace OpenIddict.Validation; +/// +/// Represents a service responsible for creating transactions. +/// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class OpenIddictValidationFactory : IOpenIddictValidationFactory { @@ -27,6 +30,7 @@ public sealed class OpenIddictValidationFactory : IOpenIddictValidationFactory _options = options ?? throw new ArgumentNullException(nameof(options)); } + /// public ValueTask CreateTransactionAsync() => new(new OpenIddictValidationTransaction { diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs index 6ab2580c..726a6dae 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlerFilters.cs @@ -16,6 +16,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireAccessTokenExtracted : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -32,6 +33,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireAccessTokenValidated : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(ProcessAuthenticationContext context) { if (context is null) @@ -48,6 +50,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireAuthorizationEntryValidationEnabled : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -64,6 +67,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireAuthorizationIdResolved : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(ValidateTokenContext context) { if (context is null) @@ -80,6 +84,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireLocalValidation : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -96,6 +101,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireIntrospectionValidation : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) @@ -112,6 +118,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireTokenIdResolved : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(ValidateTokenContext context) { if (context is null) @@ -128,6 +135,7 @@ public static class OpenIddictValidationHandlerFilters /// public sealed class RequireTokenEntryValidationEnabled : IOpenIddictValidationHandlerFilter { + /// public ValueTask IsActiveAsync(BaseContext context) { if (context is null) From 6f5c509b3faf205f67341f824467375c5ef10c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Thu, 8 Dec 2022 10:32:25 +0100 Subject: [PATCH 08/10] Update the System.Net.Http integrations to support sending a contact address via the Form HTTP header --- .../OpenIddictClientSystemNetHttpBuilder.cs | 37 +++++++++++++++- ...ctClientSystemNetHttpHandlers.Discovery.cs | 2 + ...ictClientSystemNetHttpHandlers.Exchange.cs | 1 + ...ictClientSystemNetHttpHandlers.Userinfo.cs | 1 + .../OpenIddictClientSystemNetHttpHandlers.cs | 42 +++++++++++++++++++ .../OpenIddictClientSystemNetHttpOptions.cs | 9 +++- ...penIddictValidationSystemNetHttpBuilder.cs | 39 +++++++++++++++-- ...lidationSystemNetHttpHandlers.Discovery.cs | 2 + ...tionSystemNetHttpHandlers.Introspection.cs | 1 + ...enIddictValidationSystemNetHttpHandlers.cs | 42 +++++++++++++++++++ ...penIddictValidationSystemNetHttpOptions.cs | 9 +++- 11 files changed, 178 insertions(+), 7 deletions(-) diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs index 3624017c..889e3750 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Net.Http.Headers; +using System.Net.Mail; using System.Reflection; using OpenIddict.Client.SystemNetHttp; using Polly; @@ -48,6 +49,38 @@ public sealed class OpenIddictClientSystemNetHttpBuilder return this; } + /// + /// Sets the contact address used in the "From" header that is attached + /// to the backchannel HTTP requests sent to the authorization server. + /// + /// The mail address. + /// The instance. + public OpenIddictClientSystemNetHttpBuilder SetContactAddress(MailAddress address) + { + if (address is null) + { + throw new ArgumentNullException(nameof(address)); + } + + return Configure(options => options.ContactAddress = address); + } + + /// + /// Sets the contact address used in the "From" header that is attached + /// to the backchannel HTTP requests sent to the authorization server. + /// + /// The mail address. + /// The instance. + public OpenIddictClientSystemNetHttpBuilder SetContactAddress(string address) + { + if (string.IsNullOrEmpty(address)) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0366), nameof(address)); + } + + return SetContactAddress(new MailAddress(address)); + } + /// /// Replaces the default HTTP error policy used by the OpenIddict client services. /// @@ -64,7 +97,7 @@ public sealed class OpenIddictClientSystemNetHttpBuilder } /// - /// Sets the product information used in the user agent header that is attached + /// Sets the product information used in the "User-Agent" header that is attached /// to the backchannel HTTP requests sent to the authorization server. /// /// The product information. @@ -80,7 +113,7 @@ public sealed class OpenIddictClientSystemNetHttpBuilder } /// - /// Sets the product information used in the user agent header that is attached + /// Sets the product information used in the "User-Agent" header that is attached /// to the backchannel HTTP requests sent to the authorization server. /// /// The product name. diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs index f7b5cf1b..fe7a7737 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Discovery.cs @@ -19,6 +19,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers PrepareGetHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachQueryStringParameters.Descriptor, SendHttpRequest.Descriptor, DisposeHttpRequest.Descriptor, @@ -38,6 +39,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers PrepareGetHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachQueryStringParameters.Descriptor, SendHttpRequest.Descriptor, DisposeHttpRequest.Descriptor, diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs index 039698ed..cfae0d9f 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Exchange.cs @@ -22,6 +22,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers PreparePostHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachBasicAuthenticationCredentials.Descriptor, AttachFormParameters.Descriptor, SendHttpRequest.Descriptor, diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs index 546fac3f..37ebe902 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.Userinfo.cs @@ -22,6 +22,7 @@ public static partial class OpenIddictClientSystemNetHttpHandlers PrepareGetHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachBearerAccessToken.Descriptor, AttachQueryStringParameters.Descriptor, SendHttpRequest.Descriptor, diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs index 1270a3fa..d0df4fec 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpHandlers.cs @@ -9,6 +9,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO.Compression; using System.Net.Http.Headers; +using System.Net.Mail; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -185,6 +186,47 @@ public static partial class OpenIddictClientSystemNetHttpHandlers } } + /// + /// Contains the logic responsible for attaching the contact address to the HTTP request. + /// + public sealed class AttachFromHeader : IOpenIddictClientHandler where TContext : BaseExternalContext + { + private readonly IOptionsMonitor _options; + + public AttachFromHeader(IOptionsMonitor options) + => _options = options ?? throw new ArgumentNullException(nameof(options)); + + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictClientHandlerDescriptor Descriptor { get; } + = OpenIddictClientHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler>() + .SetOrder(AttachUserAgentHeader.Descriptor.Order + 1_000) + .SetType(OpenIddictClientHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(TContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, + // this may indicate that the request was incorrectly processed by another client stack. + var request = context.Transaction.GetHttpRequestMessage() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); + + // Attach the contact address specified in the options, if available. + request.Headers.From = _options.CurrentValue.ContactAddress?.ToString(); + + return default; + } + } + /// /// Contains the logic responsible for attaching the query string parameters to the HTTP request. /// diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs index 2a77ee30..9a0d7d1f 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs @@ -6,6 +6,7 @@ using System.Net; using System.Net.Http.Headers; +using System.Net.Mail; using Polly; using Polly.Extensions.Http; @@ -25,7 +26,13 @@ public sealed class OpenIddictClientSystemNetHttpOptions .WaitAndRetryAsync(4, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))); /// - /// Gets or sets the product information used in the user agent header that is + /// Gets or sets the contact mail address used in the "From" header that is + /// attached to the backchannel HTTP requests sent to the authorization server. + /// + public MailAddress? ContactAddress { get; set; } + + /// + /// Gets or sets the product information used in the "User-Agent" header that is /// attached to the backchannel HTTP requests sent to the authorization server. /// public ProductInfoHeaderValue? ProductInformation { get; set; } diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs index b0cc178c..914960f5 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Net.Http.Headers; +using System.Net.Mail; using System.Reflection; using OpenIddict.Validation.SystemNetHttp; using Polly; @@ -48,6 +49,38 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder return this; } + /// + /// Sets the contact address used in the "From" header that is attached + /// to the backchannel HTTP requests sent to the authorization server. + /// + /// The mail address. + /// The instance. + public OpenIddictValidationSystemNetHttpBuilder SetContactAddress(MailAddress address) + { + if (address is null) + { + throw new ArgumentNullException(nameof(address)); + } + + return Configure(options => options.ContactAddress = address); + } + + /// + /// Sets the contact address used in the "From" header that is attached + /// to the backchannel HTTP requests sent to the authorization server. + /// + /// The mail address. + /// The instance. + public OpenIddictValidationSystemNetHttpBuilder SetContactAddress(string address) + { + if (string.IsNullOrEmpty(address)) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0366), nameof(address)); + } + + return SetContactAddress(new MailAddress(address)); + } + /// /// Replaces the default HTTP error policy used by the OpenIddict client services. /// @@ -64,7 +97,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder } /// - /// Sets the product information used in the user agent header that is attached + /// Sets the product information used in the "User-Agent" header that is attached /// to the backchannel HTTP requests sent to the authorization server. /// /// The product information. @@ -80,7 +113,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder } /// - /// Sets the product information used in the user agent header that is attached + /// Sets the product information used in the "User-Agent" header that is attached /// to the backchannel HTTP requests sent to the authorization server. /// /// The product name. @@ -97,7 +130,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder } /// - /// Sets the product information used in the user agent header that is attached + /// Sets the product information used in the "User-Agent" header that is attached /// to the backchannel HTTP requests sent to the authorization server based /// on the identity of the specified .NET assembly (name and version). /// diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs index b69261a2..c461994e 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Discovery.cs @@ -19,6 +19,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers PrepareGetHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachQueryStringParameters.Descriptor, SendHttpRequest.Descriptor, DisposeHttpRequest.Descriptor, @@ -38,6 +39,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers PrepareGetHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachQueryStringParameters.Descriptor, SendHttpRequest.Descriptor, DisposeHttpRequest.Descriptor, diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs index f1582426..aea375ed 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.Introspection.cs @@ -22,6 +22,7 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers PreparePostHttpRequest.Descriptor, AttachJsonAcceptHeaders.Descriptor, AttachUserAgentHeader.Descriptor, + AttachFromHeader.Descriptor, AttachBasicAuthenticationCredentials.Descriptor, AttachFormParameters.Descriptor, SendHttpRequest.Descriptor, diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs index 18612b67..a32ab895 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs @@ -9,6 +9,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO.Compression; using System.Net.Http.Headers; +using System.Net.Mail; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -186,6 +187,47 @@ public static partial class OpenIddictValidationSystemNetHttpHandlers } } + /// + /// Contains the logic responsible for attaching the contact address to the HTTP request. + /// + public sealed class AttachFromHeader : IOpenIddictValidationHandler where TContext : BaseExternalContext + { + private readonly IOptionsMonitor _options; + + public AttachFromHeader(IOptionsMonitor options) + => _options = options ?? throw new ArgumentNullException(nameof(options)); + + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictValidationHandlerDescriptor Descriptor { get; } + = OpenIddictValidationHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler>() + .SetOrder(AttachUserAgentHeader.Descriptor.Order + 1_000) + .SetType(OpenIddictValidationHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(TContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, + // this may indicate that the request was incorrectly processed by another client stack. + var request = context.Transaction.GetHttpRequestMessage() ?? + throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); + + // Attach the contact address specified in the options, if available. + request.Headers.From = _options.CurrentValue.ContactAddress?.ToString(); + + return default; + } + } + /// /// Contains the logic responsible for attaching the query string parameters to the HTTP request. /// diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs index 7cf158eb..d50946c6 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs @@ -6,6 +6,7 @@ using System.Net; using System.Net.Http.Headers; +using System.Net.Mail; using Polly; using Polly.Extensions.Http; @@ -25,7 +26,13 @@ public sealed class OpenIddictValidationSystemNetHttpOptions .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt))); /// - /// Gets or sets the product information used in the user agent header that is + /// Gets or sets the contact mail address used in the "From" header that is + /// attached to the backchannel HTTP requests sent to the authorization server. + /// + public MailAddress? ContactAddress { get; set; } + + /// + /// Gets or sets the product information used in the "User-Agent" header that is /// attached to the backchannel HTTP requests sent to the authorization server. /// public ProductInfoHeaderValue? ProductInformation { get; set; } From bf8abc6e6e07cbc0cf662cf3a7a6ae0e6529ddce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sat, 10 Dec 2022 10:45:29 +0100 Subject: [PATCH 09/10] Support OpenIddictClientOptions.ClientUri/OpenIddictServerOptions.Issuer/OpenIddictValidationOptions.Issuer URIs created with DangerousDisablePathAndQueryCanonicalization=true --- .../OpenIddictResources.resx | 3 +++ .../OpenIddictClientHandlers.Discovery.cs | 4 ++-- .../OpenIddictClientHandlers.Protection.cs | 24 +++++++++++++++++-- .../OpenIddictServerHandlers.Protection.cs | 12 +++++++++- ...OpenIddictValidationHandlers.Protection.cs | 20 ++++++++++++---- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 1cfb795b..9cc4ef7d 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1888,6 +1888,9 @@ Consider registering a certificate using 'services.AddOpenIddict().AddClient().A The request forgery protection is missing or doesn't contain the expected value. + + The issuer returned in the server configuration doesn't match the value set in the client registration options. + The '{0}' parameter shouldn't be null or empty at this point. diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs index 60298ce2..f9bd7995 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs @@ -230,8 +230,8 @@ public static partial class OpenIddictClientHandlers { context.Reject( error: Errors.ServerError, - description: SR.GetResourceString(SR.ID2098), - uri: SR.FormatID8000(SR.ID2098)); + description: SR.GetResourceString(SR.ID2165), + uri: SR.FormatID8000(SR.ID2165)); return default; } diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs index 6ab4839e..b07f4085 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Protection.cs @@ -101,7 +101,7 @@ public static partial class OpenIddictClientHandlers { null => null, - // If the client URI doesn't contain any path/query/fragment, allow both http://www.fabrikam.com + // If the client URI doesn't contain any query/fragment, allow both http://www.fabrikam.com // and http://www.fabrikam.com/ (the recommended URI representation) to be considered valid. // See https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.3 for more information. { AbsolutePath: "/", Query.Length: 0, Fragment.Length: 0 } uri => new[] @@ -110,6 +110,16 @@ public static partial class OpenIddictClientHandlers uri.AbsoluteUri[..^1] }, + // When properly normalized, Uri.AbsolutePath should never be empty and should at least + // contain a leading slash. While dangerous, System.Uri now offers a way to create a URI + // instance without applying the default canonicalization logic. To support such URIs, + // a special case is added here to add back the missing trailing slash when necessary. + { AbsolutePath.Length: 0, Query.Length: 0, Fragment.Length: 0 } uri => new[] + { + uri.AbsoluteUri, + uri.AbsoluteUri + "/" + }, + Uri uri => new[] { uri.AbsoluteUri } }; @@ -129,7 +139,7 @@ public static partial class OpenIddictClientHandlers { null => null, - // If the issuer URI doesn't contain any path/query/fragment, allow both http://www.fabrikam.com + // If the issuer URI doesn't contain any query/fragment, allow both http://www.fabrikam.com // and http://www.fabrikam.com/ (the recommended URI representation) to be considered valid. // See https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.3 for more information. { AbsolutePath: "/", Query.Length: 0, Fragment.Length: 0 } uri => new[] @@ -138,6 +148,16 @@ public static partial class OpenIddictClientHandlers uri.AbsoluteUri[..^1] }, + // When properly normalized, Uri.AbsolutePath should never be empty and should at least + // contain a leading slash. While dangerous, System.Uri now offers a way to create a URI + // instance without applying the default canonicalization logic. To support such URIs, + // a special case is added here to add back the missing trailing slash when necessary. + { AbsolutePath.Length: 0, Query.Length: 0, Fragment.Length: 0 } uri => new[] + { + uri.AbsoluteUri, + uri.AbsoluteUri + "/" + }, + Uri uri => new[] { uri.AbsoluteUri } }; diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index b2dc69b6..298f9c99 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -73,7 +73,7 @@ public static partial class OpenIddictServerHandlers { null => null, - // If the issuer URI doesn't contain any path/query/fragment, allow both http://www.fabrikam.com + // If the issuer URI doesn't contain any query/fragment, allow both http://www.fabrikam.com // and http://www.fabrikam.com/ (the recommended URI representation) to be considered valid. // See https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.3 for more information. { AbsolutePath: "/", Query.Length: 0, Fragment.Length: 0 } uri => new[] @@ -82,6 +82,16 @@ public static partial class OpenIddictServerHandlers uri.AbsoluteUri[..^1] }, + // When properly normalized, Uri.AbsolutePath should never be empty and should at least + // contain a leading slash. While dangerous, System.Uri now offers a way to create a URI + // instance without applying the default canonicalization logic. To support such URIs, + // a special case is added here to add back the missing trailing slash when necessary. + { AbsolutePath.Length: 0, Query.Length: 0, Fragment.Length: 0 } uri => new[] + { + uri.AbsoluteUri, + uri.AbsoluteUri + "/" + }, + Uri uri => new[] { uri.AbsoluteUri } }; diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs index f9b0bab8..ae518a45 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs @@ -71,16 +71,26 @@ public static partial class OpenIddictValidationHandlers // issued by a local authorization server outside a request context). null => null, - // If the issuer URI doesn't contain any path/query/fragment, allow both http://www.fabrikam.com + // If the issuer URI doesn't contain any query/fragment, allow both http://www.fabrikam.com // and http://www.fabrikam.com/ (the recommended URI representation) to be considered valid. // See https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.3 for more information. - { AbsolutePath: "/", Query.Length: 0, Fragment.Length: 0 } issuer => new[] + { AbsolutePath: "/", Query.Length: 0, Fragment.Length: 0 } uri => new[] { - issuer.AbsoluteUri, // Uri.AbsoluteUri is normalized and always contains a trailing slash. - issuer.AbsoluteUri[..^1] + uri.AbsoluteUri, // Uri.AbsoluteUri is normalized and always contains a trailing slash. + uri.AbsoluteUri[..^1] }, - Uri issuer => new[] { issuer.AbsoluteUri } + // When properly normalized, Uri.AbsolutePath should never be empty and should at least + // contain a leading slash. While dangerous, System.Uri now offers a way to create a URI + // instance without applying the default canonicalization logic. To support such URIs, + // a special case is added here to add back the missing trailing slash when necessary. + { AbsolutePath.Length: 0, Query.Length: 0, Fragment.Length: 0 } uri => new[] + { + uri.AbsoluteUri, + uri.AbsoluteUri + "/" + }, + + Uri uri => new[] { uri.AbsoluteUri } }; parameters.ValidateIssuer = parameters.ValidIssuers is not null; From 1e3884347679fc6554651f683778adb8ebdb910a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Wed, 14 Dec 2022 14:33:11 +0100 Subject: [PATCH 10/10] Bump the .NET SDK and package dependencies --- .github/workflows/build.yml | 6 +- Directory.Packages.props | 136 ++++++++++++++++++------------------ global.json | 4 +- 3 files changed, 73 insertions(+), 73 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c83b647b..4d610863 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,9 +32,9 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | - 3.1.425 - 6.0.403 - 7.0.100 + 3.1.426 + 6.0.404 + 7.0.101 - name: Setup NuGet uses: nuget/setup-nuget@v1 diff --git a/Directory.Packages.props b/Directory.Packages.props index 90d90ef3..89307dc0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,9 +34,9 @@ - - - + + + @@ -57,7 +57,7 @@ - + - - - + + + - + - + - + - + - - + + - + - - - + + +