From 8777dc99227006ba95698bc31e2071b97dce51cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Mon, 29 May 2023 19:35:01 +0200 Subject: [PATCH] Throw an exception if no CoreWindow is attached to the thread starting the challenge demand --- .../OpenIddictResources.resx | 2 +- ...lientSystemIntegrationActivationHandler.cs | 2 +- ...penIddictClientSystemIntegrationBuilder.cs | 8 +- ...ictClientSystemIntegrationConfiguration.cs | 9 +++ ...ctClientSystemIntegrationHandlerFilters.cs | 4 +- ...ystemIntegrationHandlers.Authentication.cs | 17 +++- ...penIddictClientSystemIntegrationHelpers.cs | 80 ++++++++++++++++--- 7 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 215d01de..289753d3 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1475,7 +1475,7 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId An error occurred while instantiating the embedded web server, which may indicate a permission issue. - The web authentication broker is not supported on this platform. + 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. diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs index ab070ce0..afe1f69f 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationActivationHandler.cs @@ -72,7 +72,7 @@ public sealed class OpenIddictClientSystemIntegrationActivationHandler : IHosted { #if SUPPORTS_WINDOWS_RUNTIME // On platforms that support WinRT, always favor the AppInstance.GetActivatedEventArgs() API. - if (OpenIddictClientSystemIntegrationHelpers.IsWindowsRuntimeSupported() && + if (OpenIddictClientSystemIntegrationHelpers.IsAppInstanceActivationSupported() && OpenIddictClientSystemIntegrationHelpers.GetProtocolActivationUriWithWindowsRuntime() is Uri uri) { return new OpenIddictClientSystemIntegrationActivation(uri); diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs index ffce9bb7..1133ae40 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs @@ -7,7 +7,6 @@ using System.ComponentModel; using System.IO.Pipes; using System.Net; -using System.Runtime.InteropServices; using System.Runtime.Versioning; using OpenIddict.Client.SystemIntegration; @@ -61,12 +60,7 @@ public sealed class OpenIddictClientSystemIntegrationBuilder [SupportedOSPlatform("windows10.0.17763")] public OpenIddictClientSystemIntegrationBuilder UseWebAuthenticationBroker() { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0392)); - } - - if (!OpenIddictClientSystemIntegrationHelpers.IsWindowsRuntimeSupported()) + if (!OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported()) { throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0392)); } diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs index 2aaaf2a3..3ea6e22c 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs @@ -80,6 +80,15 @@ public sealed class OpenIddictClientSystemIntegrationConfiguration : IConfigureO throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0389)); } +#pragma warning disable CA1416 + // If explicitly set, ensure the specified authentication mode is supported. + if (options.AuthenticationMode is OpenIddictClientSystemIntegrationAuthenticationMode.WebAuthenticationBroker && + !OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported()) + { + throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0392)); + } +#pragma warning restore CA1416 + // Note: the OpenIddict client system integration is currently only supported on Windows // and Linux. As such, using the system browser as the default authentication method in // conjunction with the embedded web server and activation handling should be always supported. diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs index 8d0be75a..eedb3953 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs @@ -135,7 +135,7 @@ public static class OpenIddictClientSystemIntegrationHandlerFilters } #if SUPPORTS_WINDOWS_RUNTIME - if (OpenIddictClientSystemIntegrationHelpers.IsWindowsRuntimeSupported()) + if (OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported()) { if (!context.Transaction.Properties.TryGetValue( typeof(OpenIddictClientSystemIntegrationAuthenticationMode).FullName!, out var result) || @@ -166,7 +166,7 @@ public static class OpenIddictClientSystemIntegrationHandlerFilters } #if SUPPORTS_WINDOWS_RUNTIME - if (OpenIddictClientSystemIntegrationHelpers.IsWindowsRuntimeSupported()) + if (OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported()) { return new(ContainsWebAuthenticationResult(context.Transaction)); } diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs index fe113e82..d7461b35 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs @@ -14,6 +14,7 @@ using OpenIddict.Extensions; #if SUPPORTS_WINDOWS_RUNTIME using Windows.Security.Authentication.Web; +using Windows.UI.Core; #endif namespace OpenIddict.Client.SystemIntegration; @@ -87,6 +88,20 @@ public static partial class OpenIddictClientSystemIntegrationHandlers return; } + // Note: WebAuthenticationBroker internally requires a pointer to the CoreWindow object associated + // to the thread from which the challenge operation is started. Unfortunately, CoreWindow - and by + // extension WebAuthenticationBroker - are only supported on UWP and cannot be used in Win32 apps. + // + // To ensure a meaningful exception is returned when the web authentication broker is used with an + // incompatible application model (e.g WinUI 3.0), the presence of a CoreWindow is verified here. + // + // See https://github.com/microsoft/WindowsAppSDK/issues/398 for more information. + if (!OpenIddictClientSystemIntegrationHelpers.IsWebAuthenticationBrokerSupported() || + CoreWindow.GetForCurrentThread() is null) + { + throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0392)); + } + // 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 // by launching the system browser or, as in this case, using a web-view-like approach. @@ -214,7 +229,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers // Runtime APIs and favor the Launcher.LaunchUriAsync() API when it's offered by the platform. #if SUPPORTS_WINDOWS_RUNTIME - if (OpenIddictClientSystemIntegrationHelpers.IsWindowsRuntimeSupported() && await + if (OpenIddictClientSystemIntegrationHelpers.IsUriLauncherSupported() && await OpenIddictClientSystemIntegrationHelpers.TryLaunchBrowserWithWindowsRuntimeAsync(uri)) { context.HandleRequest(); diff --git a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs index 3111208d..b7467543 100644 --- a/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs +++ b/src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHelpers.cs @@ -24,7 +24,7 @@ using Windows.System; namespace OpenIddict.Client.SystemIntegration; /// -/// Exposes companion extensions for the OpenIddict/Windows integration. +/// Exposes companion extensions for the OpenIddict/system integration. /// public static class OpenIddictClientSystemIntegrationHelpers { @@ -101,6 +101,72 @@ public static class OpenIddictClientSystemIntegrationHelpers // oldest supported version in the package, it is also used for the runtime check. internal static bool IsWindowsRuntimeSupported() => IsWindowsVersionAtLeast(10, 0, 17763); + /// + /// Determines whether WinRT app instance activation is supported on this platform. + /// + /// + /// if WinRT app instance activation is supported, otherwise. + /// + [SupportedOSPlatformGuard("windows10.0.17763")] + internal static bool IsAppInstanceActivationSupported() + { +#if SUPPORTS_WINDOWS_RUNTIME + return IsWindowsRuntimeSupported() && IsApiPresent(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsApiPresent() => ApiInformation.IsMethodPresent( + typeName : typeof(AppInstance).FullName, + methodName : nameof(AppInstance.GetActivatedEventArgs), + inputParameterCount: 0); +#else + return false; +#endif + } + + /// + /// Determines whether the WinRT URI launcher is supported on this platform. + /// + /// + /// if the WinRT URI launcher is supported, otherwise. + /// + [SupportedOSPlatformGuard("windows10.0.17763")] + internal static bool IsUriLauncherSupported() + { +#if SUPPORTS_WINDOWS_RUNTIME + return IsWindowsRuntimeSupported() && IsApiPresent(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsApiPresent() => ApiInformation.IsMethodPresent( + typeName : typeof(Launcher).FullName, + methodName : nameof(Launcher.LaunchUriAsync), + inputParameterCount: 1); +#else + return false; +#endif + } + + /// + /// Determines whether the WinRT web authentication broker is supported on this platform. + /// + /// + /// if the WinRT web authentication broker is supported, otherwise. + /// + [SupportedOSPlatformGuard("windows10.0.17763")] + internal static bool IsWebAuthenticationBrokerSupported() + { +#if SUPPORTS_WINDOWS_RUNTIME + return IsWindowsRuntimeSupported() && IsApiPresent(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsApiPresent() => ApiInformation.IsMethodPresent( + typeName : typeof(WebAuthenticationBroker).FullName, + methodName : nameof(WebAuthenticationBroker.AuthenticateAsync), + inputParameterCount: 3); +#else + return false; +#endif + } + /// /// Determines whether the specified identity contains an AppContainer /// token, indicating it's running in an AppContainer sandbox. @@ -153,12 +219,6 @@ public static class OpenIddictClientSystemIntegrationHelpers [MethodImpl(MethodImplOptions.NoInlining), SupportedOSPlatform("windows10.0.17763")] internal static Uri? GetProtocolActivationUriWithWindowsRuntime() { - if (!ApiInformation.IsMethodPresent(typeof(AppInstance).FullName, - nameof(AppInstance.GetActivatedEventArgs), inputParameterCount: 0)) - { - return null; - } - try { return AppInstance.GetActivatedEventArgs() is @@ -185,12 +245,6 @@ public static class OpenIddictClientSystemIntegrationHelpers // is implemented via a .NET Standard 2.0 TFM (which requires Windows 10 1809), it is assumed // at this point that Launcher.LaunchUriAsync() can be used in both types of applications. - if (!ApiInformation.IsMethodPresent(typeof(Launcher).FullName, - nameof(Launcher.LaunchUriAsync), inputParameterCount: 1)) - { - return false; - } - try { return await Launcher.LaunchUriAsync(uri);