diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index ee1731394..a1f5c4488 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1480,7 +1480,7 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId The web authentication broker is only supported on UWP and requires running Windows 10 version 1709 (Fall Creators) or higher. - The web authentication result cannot be resolved or contains invalid data. + The platform callback cannot be resolved or contains invalid data. The issuer attached to the static configuration must be the same as the one configured in the validation options. @@ -1692,6 +1692,9 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId Custom tabs intents are only supported on Android. + + The specified intent doesn't contain a valid data URI. + The security token is missing. diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs index b076e3800..123e4dcea 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs @@ -103,17 +103,18 @@ public static OpenIddictClientSystemIntegrationBuilder UseSystemIntegration(this // Register the built-in filters used by the default OpenIddict client system integration event handlers. builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); - builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); + builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); +#pragma warning disable CS0618 builder.Services.TryAddSingleton(); +#pragma warning restore CS0618 // Register the built-in event handlers used by the OpenIddict client system integration components. // Note: the order used here is not important, as the actual order is set in the options. diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs index 6d809c25f..dab17af12 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs @@ -17,34 +17,6 @@ namespace OpenIddict.Client.SystemIntegration; [EditorBrowsable(EditorBrowsableState.Advanced)] public static class OpenIddictClientSystemIntegrationHandlerFilters { - /// - /// Represents a filter that excludes the associated handlers if no AS web - /// authentication callback URL can be found in the transaction properties. - /// - public sealed class RequireASWebAuthenticationCallbackUrl : IOpenIddictClientHandlerFilter - { - /// - public ValueTask IsActiveAsync(BaseContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - -#if SUPPORTS_AUTHENTICATION_SERVICES && SUPPORTS_FOUNDATION - if (IsASWebAuthenticationSessionSupported()) - { - return new(ContainsASWebAuthenticationSessionResult(context.Transaction)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static bool ContainsASWebAuthenticationSessionResult(OpenIddictClientTransaction transaction) - => transaction.GetASWebAuthenticationCallbackUrl() is not null; -#endif - return new(false); - } - } - /// /// Represents a filter that excludes the associated handlers if /// the AS web authentication session integration was not enabled. @@ -133,33 +105,6 @@ public ValueTask IsActiveAsync(BaseContext context) return new(false); } } - /// - /// Represents a filter that excludes the associated handlers if no - /// custom tabs intent data can be found in the transaction properties. - /// - public sealed class RequireCustomTabsIntentData : IOpenIddictClientHandlerFilter - { - /// - public ValueTask IsActiveAsync(BaseContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - -#if SUPPORTS_ANDROID && SUPPORTS_ANDROIDX_BROWSER - if (IsCustomTabsIntentSupported()) - { - return new(ContainsCustomTabsIntentData(context.Transaction)); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static bool ContainsCustomTabsIntentData(OpenIddictClientTransaction transaction) - => transaction.GetCustomTabsIntentData() is not null; -#endif - return new(false); - } - } /// /// Represents a filter that excludes the associated handlers if the embedded web server was not enabled. @@ -217,6 +162,24 @@ public ValueTask IsActiveAsync(BaseContext context) } } + /// + /// Represents a filter that excludes the associated handlers if no + /// platform callback can be found in the transaction properties. + /// + public sealed class RequirePlatformCallback : IOpenIddictClientHandlerFilter + { + /// + public ValueTask IsActiveAsync(BaseContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + return new(context.Transaction.GetPlatformCallback() is not null); + } + } + /// /// Represents a filter that excludes the associated handlers if no protocol activation was found. /// @@ -304,6 +267,7 @@ public ValueTask IsActiveAsync(BaseContext context) /// Represents a filter that excludes the associated handlers if no /// web authentication operation was triggered during the transaction. /// + [Obsolete("This filter is obsolete and will be removed in a future version.")] public sealed class RequireWebAuthenticationResult : IOpenIddictClientHandlerFilter { /// diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs index d0adb3935..a77e63abc 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs @@ -57,9 +57,7 @@ public static class Authentication */ ExtractGetOrPostHttpListenerRequest.Descriptor, ExtractProtocolActivationParameters.Descriptor, - ExtractASWebAuthenticationCallbackUrlData.Descriptor, - ExtractCustomTabsIntentData.Descriptor, - ExtractWebAuthenticationResultData.Descriptor, + ExtractPlatformCallbackParameters.Descriptor, /* * Redirection response handling: @@ -68,9 +66,7 @@ public static class Authentication AttachCacheControlHeader.Descriptor, ProcessEmptyHttpResponse.Descriptor, ProcessProtocolActivationResponse.Descriptor, - ProcessASWebAuthenticationSessionResponse.Descriptor, - ProcessCustomTabsIntentResponse.Descriptor, - ProcessWebAuthenticationResultResponse.Descriptor + ProcessPlatformCallbackResponse.Descriptor ]); /// @@ -123,7 +119,8 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context) throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0446)); } - var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var source = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); // OpenIddict represents the complete interactive authentication dance as a two-phase process: // - The challenge, during which the user is redirected to the authorization server, either @@ -161,11 +158,11 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context) throw new InvalidOperationException(SR.GetResourceString(SR.ID0448)); } - NSUrl url; + OpenIddictClientSystemIntegrationPlatformCallback callback; try { - url = await source.Task.WaitAsync(context.CancellationToken); + callback = await source.Task.WaitAsync(context.CancellationToken); } // Since the result of this operation is known by the time the task signaled by ASWebAuthenticationSession @@ -195,7 +192,7 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context) return; } - await _service.HandleASWebAuthenticationCallbackUrlAsync(url, context.CancellationToken); + await _service.HandlePlatformCallbackAsync(callback, context.CancellationToken); context.HandleRequest(); return; @@ -250,7 +247,43 @@ void HandleCallback(NSUrl? url, NSError? error) { if (url is not null) { - source.SetResult(url); + var parameters = new Dictionary(StringComparer.Ordinal); + + if (!string.IsNullOrEmpty(url.Query)) + { + foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + // Note: the fragment is always processed after the query string to ensure that + // parameters extracted from the fragment are preferred to parameters extracted + // from the query string when they are present in both parts. + + if (!string.IsNullOrEmpty(url.Fragment)) + { + foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + source.SetResult(new OpenIddictClientSystemIntegrationPlatformCallback(url!, parameters) + { + // Attach the raw URL to the callback properties. + Properties = { [typeof(NSUrl).FullName!] = url } + }); } else if (error is not null) @@ -426,8 +459,47 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context) parameter => new StringValues((string?[]?) parameter.Value))), callbackUri: new Uri(context.RedirectUri, UriKind.Absolute))) { - case { ResponseStatus: WebAuthenticationStatus.Success } result: - await _service.HandleWebAuthenticationResultAsync(result, context.CancellationToken); + case { ResponseStatus: WebAuthenticationStatus.Success } result + when Uri.TryCreate(result.ResponseData, UriKind.Absolute, out Uri? uri): + var parameters = new Dictionary(StringComparer.Ordinal); + + if (!string.IsNullOrEmpty(uri.Query)) + { + foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + // Note: the fragment is always processed after the query string to ensure that + // parameters extracted from the fragment are preferred to parameters extracted + // from the query string when they are present in both parts. + + if (!string.IsNullOrEmpty(uri.Fragment)) + { + foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + var callback = new OpenIddictClientSystemIntegrationPlatformCallback(uri, parameters) + { + // Attach the authentication result to the properties. + Properties = { [typeof(WebAuthenticationResult).FullName!] = result } + }; + + await _service.HandlePlatformCallbackAsync(callback, context.CancellationToken); context.HandleRequest(); return; diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs index ac0067deb..cef46795c 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Session.cs @@ -57,9 +57,7 @@ public static class Session */ ExtractGetOrPostHttpListenerRequest.Descriptor, ExtractProtocolActivationParameters.Descriptor, - ExtractASWebAuthenticationCallbackUrlData.Descriptor, - ExtractCustomTabsIntentData.Descriptor, - ExtractWebAuthenticationResultData.Descriptor, + ExtractPlatformCallbackParameters.Descriptor, /* * Post-logout redirection response handling: @@ -68,9 +66,7 @@ public static class Session AttachCacheControlHeader.Descriptor, ProcessEmptyHttpResponse.Descriptor, ProcessProtocolActivationResponse.Descriptor, - ProcessASWebAuthenticationSessionResponse.Descriptor, - ProcessCustomTabsIntentResponse.Descriptor, - ProcessWebAuthenticationResultResponse.Descriptor + ProcessPlatformCallbackResponse.Descriptor ]); /// @@ -123,7 +119,8 @@ public async ValueTask HandleAsync(ApplyLogoutRequestContext context) throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0446)); } - var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var source = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); // OpenIddict represents the complete interactive logout dance as a two-phase process: // - The sign-out, during which the user is redirected to the authorization server, either @@ -161,11 +158,11 @@ public async ValueTask HandleAsync(ApplyLogoutRequestContext context) throw new InvalidOperationException(SR.GetResourceString(SR.ID0448)); } - NSUrl url; + OpenIddictClientSystemIntegrationPlatformCallback callback; try { - url = await source.Task.WaitAsync(context.CancellationToken); + callback = await source.Task.WaitAsync(context.CancellationToken); } // Since the result of this operation is known by the time the task signaled by ASWebAuthenticationSession @@ -195,7 +192,7 @@ public async ValueTask HandleAsync(ApplyLogoutRequestContext context) return; } - await _service.HandleASWebAuthenticationCallbackUrlAsync(url, context.CancellationToken); + await _service.HandlePlatformCallbackAsync(callback, context.CancellationToken); context.HandleRequest(); return; @@ -250,7 +247,43 @@ void HandleCallback(NSUrl? url, NSError? error) { if (url is not null) { - source.SetResult(url); + var parameters = new Dictionary(StringComparer.Ordinal); + + if (!string.IsNullOrEmpty(url.Query)) + { + foreach (var parameter in OpenIddictHelpers.ParseQuery(url.Query)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + // Note: the fragment is always processed after the query string to ensure that + // parameters extracted from the fragment are preferred to parameters extracted + // from the query string when they are present in both parts. + + if (!string.IsNullOrEmpty(url.Fragment)) + { + foreach (var parameter in OpenIddictHelpers.ParseFragment(url.Fragment)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + source.SetResult(new OpenIddictClientSystemIntegrationPlatformCallback(url!, parameters) + { + // Attach the raw URL to the callback properties. + Properties = { [typeof(NSUrl).FullName!] = url } + }); } else if (error is not null) @@ -426,8 +459,47 @@ public async ValueTask HandleAsync(ApplyLogoutRequestContext context) parameter => new StringValues((string?[]?) parameter.Value))), callbackUri: new Uri(context.PostLogoutRedirectUri, UriKind.Absolute))) { - case { ResponseStatus: WebAuthenticationStatus.Success } result: - await _service.HandleWebAuthenticationResultAsync(result, context.CancellationToken); + case { ResponseStatus: WebAuthenticationStatus.Success } result + when Uri.TryCreate(result.ResponseData, UriKind.Absolute, out Uri? uri): + var parameters = new Dictionary(StringComparer.Ordinal); + + if (!string.IsNullOrEmpty(uri.Query)) + { + foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + // Note: the fragment is always processed after the query string to ensure that + // parameters extracted from the fragment are preferred to parameters extracted + // from the query string when they are present in both parts. + + if (!string.IsNullOrEmpty(uri.Fragment)) + { + foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + var callback = new OpenIddictClientSystemIntegrationPlatformCallback(uri, parameters) + { + // Attach the authentication result to the properties. + Properties = { [typeof(WebAuthenticationResult).FullName!] = result } + }; + + await _service.HandlePlatformCallbackAsync(callback, context.CancellationToken); context.HandleRequest(); return; diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs index aad73fa1b..290b5aede 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Net; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; using System.Security.Claims; using System.Text; using Microsoft.Extensions.Hosting; @@ -24,18 +23,6 @@ using IHostApplicationLifetime = Microsoft.Extensions.Hosting.IApplicationLifetime; #endif -#if SUPPORTS_ANDROID -using NativeUri = Android.Net.Uri; -#endif - -#if SUPPORTS_FOUNDATION -using Foundation; -#endif - -#if SUPPORTS_WINDOWS_RUNTIME -using Windows.Security.Authentication.Web; -#endif - namespace OpenIddict.Client.SystemIntegration; [EditorBrowsable(EditorBrowsableState.Never)] @@ -47,9 +34,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers */ ResolveRequestUriFromHttpListenerRequest.Descriptor, ResolveRequestUriFromProtocolActivation.Descriptor, - ResolveRequestUriFromASWebAuthenticationCallbackUrl.Descriptor, - ResolveRequestUriFromCustomTabsIntentData.Descriptor, - ResolveRequestUriFromWebAuthenticationResult.Descriptor, + ResolveRequestUriFromPlatformCallback.Descriptor, InferEndpointTypeFromDynamicAddress.Descriptor, RejectUnknownHttpRequests.Descriptor, @@ -215,26 +200,23 @@ public ValueTask HandleAsync(ProcessRequestContext context) } /// - /// Contains the logic responsible for resolving the request URI from the AS web authentication session callback URL. - /// Note: this handler is not used when the OpenID Connect request is not an AS web authentication session callback URL. + /// Contains the logic responsible for resolving the request URI from the platform callback details. + /// Note: this handler is not used when the OpenID Connect request is not a platform callback. /// - public sealed class ResolveRequestUriFromASWebAuthenticationCallbackUrl : IOpenIddictClientHandler + public sealed class ResolveRequestUriFromPlatformCallback : IOpenIddictClientHandler { /// /// Gets the default descriptor definition assigned to this handler. /// public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() - .AddFilter() - .UseSingletonHandler() + .AddFilter() + .UseSingletonHandler() .SetOrder(ResolveRequestUriFromProtocolActivation.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); /// - [SupportedOSPlatform("ios12.0")] - [SupportedOSPlatform("maccatalyst13.1")] - [SupportedOSPlatform("macos10.15")] public ValueTask HandleAsync(ProcessRequestContext context) { if (context is null) @@ -242,10 +224,9 @@ public ValueTask HandleAsync(ProcessRequestContext context) throw new ArgumentNullException(nameof(context)); } -#if SUPPORTS_AUTHENTICATION_SERVICES && SUPPORTS_FOUNDATION - (context.BaseUri, context.RequestUri) = context.Transaction.GetASWebAuthenticationCallbackUrl() switch + (context.BaseUri, context.RequestUri) = context.Transaction.GetPlatformCallback() switch { - NSUrl url when Uri.TryCreate(url.AbsoluteString, UriKind.Absolute, out Uri? uri) => ( + { CallbackUri: Uri uri } => ( BaseUri: new UriBuilder(uri) { Path = null, Query = null, Fragment = null }.Uri, RequestUri: uri), @@ -253,52 +234,6 @@ NSUrl url when Uri.TryCreate(url.AbsoluteString, UriKind.Absolute, out Uri? uri) }; return default; -#else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0446)); -#endif - } - } - - /// - /// Contains the logic responsible for resolving the request URI from the custom tabs intent data. - /// Note: this handler is not used when the OpenID Connect request is not an custom tabs intent callback. - /// - public sealed class ResolveRequestUriFromCustomTabsIntentData : IOpenIddictClientHandler - { - /// - /// Gets the default descriptor definition assigned to this handler. - /// - public static OpenIddictClientHandlerDescriptor Descriptor { get; } - = OpenIddictClientHandlerDescriptor.CreateBuilder() - .AddFilter() - .UseSingletonHandler() - .SetOrder(ResolveRequestUriFromASWebAuthenticationCallbackUrl.Descriptor.Order + 1_000) - .SetType(OpenIddictClientHandlerType.BuiltIn) - .Build(); - - /// - [SupportedOSPlatform("android21.0")] - public ValueTask HandleAsync(ProcessRequestContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - -#if SUPPORTS_ANDROID - (context.BaseUri, context.RequestUri) = context.Transaction.GetCustomTabsIntentData() switch - { - NativeUri url when Uri.TryCreate(url.ToString(), UriKind.Absolute, out Uri? uri) => ( - BaseUri: new UriBuilder(uri) { Path = null, Query = null, Fragment = null }.Uri, - RequestUri: uri), - - _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0393)) - }; - - return default; -#else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0452)); -#endif } } @@ -306,6 +241,7 @@ NativeUri url when Uri.TryCreate(url.ToString(), UriKind.Absolute, out Uri? uri) /// Contains the logic responsible for resolving the request URI from the web authentication result. /// Note: this handler is not used when the OpenID Connect request is not a web authentication result. /// + [Obsolete("This event handler is obsolete and will be removed in a future version.")] public sealed class ResolveRequestUriFromWebAuthenticationResult : IOpenIddictClientHandler { /// @@ -315,35 +251,13 @@ public sealed class ResolveRequestUriFromWebAuthenticationResult : IOpenIddictCl = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler() - .SetOrder(ResolveRequestUriFromCustomTabsIntentData.Descriptor.Order + 1_000) + .SetOrder(ResolveRequestUriFromPlatformCallback.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); /// - [SupportedOSPlatform("windows10.0.17763")] public ValueTask HandleAsync(ProcessRequestContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - -#if SUPPORTS_WINDOWS_RUNTIME - (context.BaseUri, context.RequestUri) = context.Transaction.GetWebAuthenticationResult() switch - { - { ResponseStatus: WebAuthenticationStatus.Success, ResponseData: string data } when - Uri.TryCreate(data, UriKind.Absolute, out Uri? uri) => ( - BaseUri: new UriBuilder(uri) { Path = null, Query = null, Fragment = null }.Uri, - RequestUri: uri), - - _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0393)) - }; - - return default; -#else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0392)); -#endif - } + => throw new NotSupportedException(SR.GetResourceString(SR.ID0403)); } /// @@ -671,26 +585,23 @@ public ValueTask HandleAsync(TContext context) } /// - /// Contains the logic responsible for extracting OpenID Connect requests from the callback URL of an AS session. - /// Note: this handler is not used when the OpenID Connect request is not an AS web authentication session callback. + /// Contains the logic responsible for extracting OpenID Connect requests from the URI of a platform callback. + /// Note: this handler is not used when the OpenID Connect request is not a platform callback. /// - public sealed class ExtractASWebAuthenticationCallbackUrlData : IOpenIddictClientHandler where TContext : BaseValidatingContext + public sealed class ExtractPlatformCallbackParameters : IOpenIddictClientHandler where TContext : BaseValidatingContext { /// /// Gets the default descriptor definition assigned to this handler. /// public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() - .AddFilter() - .UseSingletonHandler>() + .AddFilter() + .UseSingletonHandler>() .SetOrder(ExtractProtocolActivationParameters.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); /// - [SupportedOSPlatform("ios12.0")] - [SupportedOSPlatform("maccatalyst13.1")] - [SupportedOSPlatform("macos10.15")] public ValueTask HandleAsync(TContext context) { if (context is null) @@ -698,105 +609,14 @@ public ValueTask HandleAsync(TContext context) throw new ArgumentNullException(nameof(context)); } -#if SUPPORTS_AUTHENTICATION_SERVICES && SUPPORTS_FOUNDATION - if (context.Transaction.GetASWebAuthenticationCallbackUrl() - is not NSUrl url || !Uri.TryCreate(url.AbsoluteString, UriKind.Absolute, out Uri? uri)) + if (context.Transaction.GetPlatformCallback() is not OpenIddictClientSystemIntegrationPlatformCallback callback) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0393)); } - var parameters = new Dictionary(StringComparer.Ordinal); - - if (!string.IsNullOrEmpty(uri.Query)) - { - foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) - { - parameters[parameter.Key] = parameter.Value; - } - } - - // Note: the fragment is always processed after the query string to ensure that - // parameters extracted from the fragment are preferred to parameters extracted - // from the query string when they are present in both parts. - - if (!string.IsNullOrEmpty(uri.Fragment)) - { - foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) - { - parameters[parameter.Key] = parameter.Value; - } - } - - context.Transaction.Request = new OpenIddictRequest(parameters); + context.Transaction.Request = new OpenIddictRequest(callback.Parameters); return default; -#else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0446)); -#endif - } - } - - /// - /// Contains the logic responsible for extracting OpenID Connect requests from the callback URL of a custom tabs intent. - /// Note: this handler is not used when the OpenID Connect request is not a custom tabs intent result. - /// - public sealed class ExtractCustomTabsIntentData : IOpenIddictClientHandler where TContext : BaseValidatingContext - { - /// - /// Gets the default descriptor definition assigned to this handler. - /// - public static OpenIddictClientHandlerDescriptor Descriptor { get; } - = OpenIddictClientHandlerDescriptor.CreateBuilder() - .AddFilter() - .UseSingletonHandler>() - .SetOrder(ExtractProtocolActivationParameters.Descriptor.Order + 1_000) - .SetType(OpenIddictClientHandlerType.BuiltIn) - .Build(); - - /// - [SupportedOSPlatform("android21.0")] - public ValueTask HandleAsync(TContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - -#if SUPPORTS_ANDROID - if (context.Transaction.GetCustomTabsIntentData() - is not NativeUri url || !Uri.TryCreate(url.ToString(), UriKind.Absolute, out Uri? uri)) - { - throw new InvalidOperationException(SR.GetResourceString(SR.ID0393)); - } - - var parameters = new Dictionary(StringComparer.Ordinal); - - if (!string.IsNullOrEmpty(uri.Query)) - { - foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) - { - parameters[parameter.Key] = parameter.Value; - } - } - - // Note: the fragment is always processed after the query string to ensure that - // parameters extracted from the fragment are preferred to parameters extracted - // from the query string when they are present in both parts. - - if (!string.IsNullOrEmpty(uri.Fragment)) - { - foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) - { - parameters[parameter.Key] = parameter.Value; - } - } - - context.Transaction.Request = new OpenIddictRequest(parameters); - - return default; -#else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0446)); -#endif } } @@ -805,6 +625,7 @@ public ValueTask HandleAsync(TContext context) /// requests from the response data of a web authentication result. /// Note: this handler is not used when the OpenID Connect request is not a web authentication result. /// + [Obsolete("This event handler is obsolete and will be removed in a future version.")] public sealed class ExtractWebAuthenticationResultData : IOpenIddictClientHandler where TContext : BaseValidatingContext { /// @@ -814,56 +635,13 @@ public sealed class ExtractWebAuthenticationResultData : IOpenIddictCl = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler>() - .SetOrder(ExtractCustomTabsIntentData.Descriptor.Order + 1_000) + .SetOrder(ExtractPlatformCallbackParameters.Descriptor.Order + 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); /// - [SupportedOSPlatform("windows10.0.17763")] public ValueTask HandleAsync(TContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - -#if SUPPORTS_WINDOWS_RUNTIME - if (context.Transaction.GetWebAuthenticationResult() - is not { ResponseStatus: WebAuthenticationStatus.Success, ResponseData: string data } || - !Uri.TryCreate(data, UriKind.Absolute, out Uri? uri)) - { - throw new InvalidOperationException(SR.GetResourceString(SR.ID0393)); - } - - var parameters = new Dictionary(StringComparer.Ordinal); - - if (!string.IsNullOrEmpty(uri.Query)) - { - foreach (var parameter in OpenIddictHelpers.ParseQuery(uri.Query)) - { - parameters[parameter.Key] = parameter.Value; - } - } - - // Note: the fragment is always processed after the query string to ensure that - // parameters extracted from the fragment are preferred to parameters extracted - // from the query string when they are present in both parts. - - if (!string.IsNullOrEmpty(uri.Fragment)) - { - foreach (var parameter in OpenIddictHelpers.ParseFragment(uri.Fragment)) - { - parameters[parameter.Key] = parameter.Value; - } - } - - context.Transaction.Request = new OpenIddictRequest(parameters); - - return default; -#else - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0392)); -#endif - } + => throw new NotSupportedException(SR.GetResourceString(SR.ID0403)); } /// @@ -2056,7 +1834,7 @@ public async ValueTask HandleAsync(ProcessChallengeContext context) // the server support response_mode=fragment, use it if the response types contain // a value that prevents response_mode=query from being used (token/id_token). ({ Count: > 0 } client, { Count: > 0 } server) when - OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported() && + IsWebAuthenticationBrokerSupported() && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.WebAuthenticationBroker) && client.Contains(ResponseModes.Fragment) && server.Contains(ResponseModes.Fragment) && (types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token)) @@ -2067,7 +1845,7 @@ public async ValueTask HandleAsync(ProcessChallengeContext context) // assume it is supported and use it if the response types contain a value that // prevents response_mode=query from being used (token/id_token). ({ Count: > 0 } client, { Count: 0 }) when - OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported() && + IsWebAuthenticationBrokerSupported() && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.WebAuthenticationBroker) && client.Contains(ResponseModes.Fragment) && (types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token)) @@ -2129,7 +1907,7 @@ await IsEmbeddedWebServerRedirectUriAsync() // When using the web authentication broker on Windows, if both // the client and the server support response_mode=fragment, use it. ({ Count: > 0 } client, { Count: > 0 } server) when - OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported() && + IsWebAuthenticationBrokerSupported() && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.WebAuthenticationBroker) && client.Contains(ResponseModes.Fragment) && server.Contains(ResponseModes.Fragment) => ResponseModes.Fragment, @@ -2610,43 +2388,7 @@ public sealed class ProcessProtocolActivationResponse : IOpenIddictCli = OpenIddictClientHandlerDescriptor.CreateBuilder() .AddFilter() .UseSingletonHandler>() - .SetOrder(ProcessWebAuthenticationResultResponse.Descriptor.Order - 1_000) - .SetType(OpenIddictClientHandlerType.BuiltIn) - .Build(); - - /// - public ValueTask HandleAsync(TContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - - // For both protocol activations (initial or redirected) and web-view-like results, - // no proper response can be generated and eventually displayed to the user. In this - // case, simply stop processing the response and mark the request as fully handled. - // - // Note: this logic applies to both successful and errored responses. - - context.HandleRequest(); - return default; - } - } - - /// - /// Contains the logic responsible for marking OpenID Connect responses returned via a custom tabs intent web as processed. - /// - public sealed class ProcessCustomTabsIntentResponse : IOpenIddictClientHandler - where TContext : BaseRequestContext - { - /// - /// Gets the default descriptor definition assigned to this handler. - /// - public static OpenIddictClientHandlerDescriptor Descriptor { get; } - = OpenIddictClientHandlerDescriptor.CreateBuilder() - .AddFilter() - .UseSingletonHandler>() - .SetOrder(int.MaxValue) + .SetOrder(ProcessPlatformCallbackResponse.Descriptor.Order - 1_000) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -2670,10 +2412,9 @@ public ValueTask HandleAsync(TContext context) } /// - /// Contains the logic responsible for marking OpenID Connect responses - /// returned via AS web authentication callback URLs as processed. + /// Contains the logic responsible for marking OpenID Connect responses returned via a platform callback. /// - public sealed class ProcessASWebAuthenticationSessionResponse : IOpenIddictClientHandler + public sealed class ProcessPlatformCallbackResponse : IOpenIddictClientHandler where TContext : BaseRequestContext { /// @@ -2681,8 +2422,8 @@ public sealed class ProcessASWebAuthenticationSessionResponse : IOpenI /// public static OpenIddictClientHandlerDescriptor Descriptor { get; } = OpenIddictClientHandlerDescriptor.CreateBuilder() - .AddFilter() - .UseSingletonHandler>() + .AddFilter() + .UseSingletonHandler>() .SetOrder(int.MaxValue) .SetType(OpenIddictClientHandlerType.BuiltIn) .Build(); @@ -2710,6 +2451,7 @@ public ValueTask HandleAsync(TContext context) /// Contains the logic responsible for marking OpenID Connect /// responses returned via web authentication results as processed. /// + [Obsolete("This handler is obsolete and will be removed in a future version.")] public sealed class ProcessWebAuthenticationResultResponse : IOpenIddictClientHandler where TContext : BaseRequestContext { diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs index f29678dd8..ccb11fe3a 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs @@ -44,6 +44,14 @@ namespace OpenIddict.Client.SystemIntegration; /// public static class OpenIddictClientSystemIntegrationHelpers { + /// + /// Gets the associated with the current context. + /// + /// The transaction instance. + /// The instance or if it couldn't be found. + public static OpenIddictClientSystemIntegrationPlatformCallback? GetPlatformCallback(this OpenIddictClientTransaction transaction) + => transaction.GetProperty(typeof(OpenIddictClientSystemIntegrationPlatformCallback).FullName!); + /// /// Gets the associated with the current context. /// @@ -60,39 +68,17 @@ public static class OpenIddictClientSystemIntegrationHelpers public static HttpListenerContext? GetHttpListenerContext(this OpenIddictClientTransaction transaction) => transaction.GetProperty(typeof(HttpListenerContext).FullName!); -#if SUPPORTS_AUTHENTICATION_SERVICES && SUPPORTS_FOUNDATION - /// - /// Gets the AS web authentication callback URL associated with the current context. - /// - /// The transaction instance. - /// The instance or if it couldn't be found. - [SupportedOSPlatform("ios12.0")] - [SupportedOSPlatform("maccatalyst13.1")] - [SupportedOSPlatform("macos10.15")] - public static NSUrl? GetASWebAuthenticationCallbackUrl(this OpenIddictClientTransaction transaction) - => transaction.GetProperty(typeof(NSUrl).FullName!); -#endif - -#if SUPPORTS_ANDROID && SUPPORTS_ANDROIDX_BROWSER - /// - /// Gets the custom tabs intent data associated with the current context. - /// - /// The transaction instance. - /// The instance or if it couldn't be found. - [SupportedOSPlatform("android21.0")] - public static NativeUri? GetCustomTabsIntentData(this OpenIddictClientTransaction transaction) - => transaction.GetProperty(typeof(NativeUri).FullName!); -#endif - #if SUPPORTS_WINDOWS_RUNTIME /// /// Gets the associated with the current context. /// /// The transaction instance. /// The instance or if it couldn't be found. - [SupportedOSPlatform("windows10.0.17763")] + [Obsolete("This extension is obsolete and will be removed in a future version."), SupportedOSPlatform("windows10.0.17763")] public static WebAuthenticationResult? GetWebAuthenticationResult(this OpenIddictClientTransaction transaction) - => transaction.GetProperty(typeof(WebAuthenticationResult).FullName!); + => transaction.GetPlatformCallback() is OpenIddictClientSystemIntegrationPlatformCallback callback && + callback.Properties.TryGetValue(typeof(WebAuthenticationResult).FullName!, out object? property) && + property is WebAuthenticationResult result ? result : null; #endif /// diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationPlatformCallback.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationPlatformCallback.cs new file mode 100644 index 000000000..66efa756d --- /dev/null +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationPlatformCallback.cs @@ -0,0 +1,62 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System.Collections.Immutable; +using System.ComponentModel; +using OpenIddict.Extensions; + +namespace OpenIddict.Client.SystemIntegration; + +/// +/// Represents a generic platform callback. +/// +[EditorBrowsable(EditorBrowsableState.Advanced)] +public sealed class OpenIddictClientSystemIntegrationPlatformCallback +{ + /// + /// Creates a new instance of the class. + /// + /// The callback URI. + /// The callback parameters. + /// is . + /// is . + public OpenIddictClientSystemIntegrationPlatformCallback( + Uri uri, IReadOnlyDictionary parameters) + { + if (uri is null) + { + throw new ArgumentNullException(nameof(uri)); + } + + if (!uri.IsAbsoluteUri || OpenIddictHelpers.IsImplicitFileUri(uri)) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0144), nameof(uri)); + } + + if (parameters is null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + CallbackUri = uri; + Parameters = parameters.ToImmutableDictionary(); + } + + /// + /// Gets the callback URI. + /// + public Uri CallbackUri { get; } + + /// + /// Gets the parameters attached to this instance. + /// + public ImmutableDictionary Parameters { get; } + + /// + /// Gets the additional properties attached to this instance. + /// + public Dictionary Properties { get; } = new(StringComparer.Ordinal); +} diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs index 729cf17cc..606c5b4c7 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationService.cs @@ -7,22 +7,13 @@ using System.ComponentModel; using System.IO.Pipes; using System.Net; -using System.Runtime.Versioning; using System.Security.Principal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using OpenIddict.Extensions; #if SUPPORTS_ANDROID using Android.Content; -using NativeUri = Android.Net.Uri; -#endif - -#if SUPPORTS_FOUNDATION -using Foundation; -#endif - -#if SUPPORTS_WINDOWS_RUNTIME -using Windows.Security.Authentication.Web; #endif namespace OpenIddict.Client.SystemIntegration; @@ -51,18 +42,6 @@ public OpenIddictClientSystemIntegrationService( _provider = provider ?? throw new ArgumentNullException(nameof(provider)); } - /// - /// Handles the specified protocol activation. - /// - /// The protocol activation details. - /// The that can be used to abort the operation. - /// A that can be used to monitor the asynchronous operation. - /// is . - [EditorBrowsable(EditorBrowsableState.Advanced)] - public Task HandleProtocolActivationAsync( - OpenIddictClientSystemIntegrationActivation activation, CancellationToken cancellationToken = default) - => HandleRequestAsync(activation ?? throw new ArgumentNullException(nameof(activation)), cancellationToken); - #if SUPPORTS_ANDROID && SUPPORTS_ANDROIDX_BROWSER /// /// Handles the specified intent. @@ -73,46 +52,93 @@ public Task HandleProtocolActivationAsync( /// is . [EditorBrowsable(EditorBrowsableState.Advanced)] public Task HandleCustomTabsIntentAsync(Intent intent, CancellationToken cancellationToken = default) - => HandleRequestAsync(intent?.Data ?? throw new ArgumentNullException(nameof(intent)), cancellationToken); + { + if (intent is null) + { + throw new ArgumentNullException(nameof(intent)); + } + + if (intent.Data is null) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0453), nameof(intent)); + } + + var parameters = new Dictionary(StringComparer.Ordinal); + + if (!string.IsNullOrEmpty(intent.Data.Query)) + { + foreach (var parameter in OpenIddictHelpers.ParseQuery(intent.Data.Query)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + // Note: the fragment is always processed after the query string to ensure that + // parameters extracted from the fragment are preferred to parameters extracted + // from the query string when they are present in both parts. + + if (!string.IsNullOrEmpty(intent.Data.Fragment)) + { + foreach (var parameter in OpenIddictHelpers.ParseFragment(intent.Data.Fragment)) + { + parameters[parameter.Key] = parameter.Value.Count switch + { + 0 => default, + 1 => parameter.Value[0], + _ => parameter.Value.ToArray() + }; + } + } + + var uri = new Uri(intent.Data.ToString()!, UriKind.Absolute); + var callback = new OpenIddictClientSystemIntegrationPlatformCallback(uri, parameters) + { + // Attach the intent to the properties. + Properties = { [typeof(Intent).FullName!] = intent } + }; + + return HandlePlatformCallbackAsync(callback, cancellationToken); + } #endif /// - /// Handles the specified HTTP request. + /// Handles the specified platform callback. /// - /// The HTTP request received by the embedded web server. + /// The platform callback details. /// The that can be used to abort the operation. /// A that can be used to monitor the asynchronous operation. - /// is . - internal Task HandleHttpRequestAsync(HttpListenerContext request, CancellationToken cancellationToken = default) - => HandleRequestAsync(request ?? throw new ArgumentNullException(nameof(request)), cancellationToken); + /// is . + [EditorBrowsable(EditorBrowsableState.Never)] + public Task HandlePlatformCallbackAsync( + OpenIddictClientSystemIntegrationPlatformCallback callback, CancellationToken cancellationToken = default) + => HandleRequestAsync(callback ?? throw new ArgumentNullException(nameof(callback)), cancellationToken); -#if SUPPORTS_AUTHENTICATION_SERVICES && SUPPORTS_FOUNDATION /// - /// Handles the specified AS web authentication session callback URL. + /// Handles the specified protocol activation. /// - /// The AS web authentication session callback URL. + /// The protocol activation details. /// The that can be used to abort the operation. /// A that can be used to monitor the asynchronous operation. - /// is . - [SupportedOSPlatform("ios12.0")] - [SupportedOSPlatform("maccatalyst13.1")] - [SupportedOSPlatform("macos10.15")] - internal Task HandleASWebAuthenticationCallbackUrlAsync(NSUrl url, CancellationToken cancellationToken = default) - => HandleRequestAsync(url, cancellationToken); -#endif + /// is . + [EditorBrowsable(EditorBrowsableState.Advanced)] + public Task HandleProtocolActivationAsync( + OpenIddictClientSystemIntegrationActivation activation, CancellationToken cancellationToken = default) + => HandleRequestAsync(activation ?? throw new ArgumentNullException(nameof(activation)), cancellationToken); -#if SUPPORTS_WINDOWS_RUNTIME /// - /// Handles the specified web authentication result. + /// Handles the specified HTTP request. /// - /// The web authentication result. + /// The HTTP request received by the embedded web server. /// The that can be used to abort the operation. /// A that can be used to monitor the asynchronous operation. - /// is . - [SupportedOSPlatform("windows10.0.17763")] - internal Task HandleWebAuthenticationResultAsync(WebAuthenticationResult result, CancellationToken cancellationToken = default) - => HandleRequestAsync(result ?? throw new ArgumentNullException(nameof(result)), cancellationToken); -#endif + /// is . + internal Task HandleHttpRequestAsync(HttpListenerContext request, CancellationToken cancellationToken = default) + => HandleRequestAsync(request ?? throw new ArgumentNullException(nameof(request)), cancellationToken); /// /// Handles the request using the specified property. diff --git a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs index db2f4afa2..58d5c6bd3 100644 --- a/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs +++ b/src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs @@ -263,7 +263,7 @@ public ValueTask HandleAsync(HandleConfigurationResponseContext context) /// Contains the logic responsible for amending the client authentication methods /// supported by the device authorization endpoint for the providers that require it. /// - [Obsolete("This class is obsolete and will be removed in a future version.", error: true)] + [Obsolete("This event handler is obsolete and will be removed in a future version.", error: true)] public sealed class AmendDeviceAuthorizationEndpointClientAuthenticationMethods : IOpenIddictClientHandler { /// @@ -285,7 +285,7 @@ public ValueTask HandleAsync(HandleConfigurationResponseContext context) /// Contains the logic responsible for amending the client authentication /// methods supported by the token endpoint for the providers that require it. /// - [Obsolete("This class is obsolete and will be removed in a future version.", error: true)] + [Obsolete("This event handler is obsolete and will be removed in a future version.", error: true)] public sealed class AmendTokenEndpointClientAuthenticationMethods : IOpenIddictClientHandler { /// diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs index 07bd994ce..3f91fbd18 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs @@ -1562,7 +1562,7 @@ public async ValueTask HandleAsync(GenerateTokenContext context) /// Contains the logic responsible for beautifying user-typed tokens. /// Note: this handler is not used when the degraded mode is enabled. /// - [Obsolete("This class is obsolete and will be removed in a future version.", error: true)] + [Obsolete("This event handler is obsolete and will be removed in a future version.", error: true)] public sealed class BeautifyToken : IOpenIddictServerHandler { ///