Skip to content

Commit c887672

Browse files
committed
Support using Process.Start() on macOS and reintroduce the runtime checks preventing the portable version of OpenIddict.Client.SystemIntegration from being used on Android, iOS and Mac Catalyst
1 parent 738256c commit c887672

10 files changed

+117
-64
lines changed

sandbox/OpenIddict.Sandbox.WinForms.Client/Worker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
2727
// in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer).
2828
//
2929
// Alternatively, the application can be packaged and use windows.protocol to
30-
// register the protocol handler/custom URI scheme with the operation system.
30+
// register the protocol handler/custom URI scheme with the operating system.
3131
using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.winforms.client");
3232
root.SetValue(string.Empty, "URL:com.openiddict.sandbox.winforms.client");
3333
root.SetValue("URL Protocol", string.Empty);

sandbox/OpenIddict.Sandbox.Wpf.Client/Worker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
2727
// in HKEY_CLASSES_ROOT (in this case, it should be added by a dedicated installer).
2828
//
2929
// Alternatively, the application can be packaged and use windows.protocol to
30-
// register the protocol handler/custom URI scheme with the operation system.
30+
// register the protocol handler/custom URI scheme with the operating system.
3131
using var root = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\com.openiddict.sandbox.wpf.client");
3232
root.SetValue(string.Empty, "URL:com.openiddict.sandbox.wpf.client");
3333
root.SetValue("URL Protocol", string.Empty);

src/OpenIddict.Abstractions/OpenIddictResources.resx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId
15601560
<value>An explicit grant type must be attached when specifying a specific response type (except when using the special response_type=none value).</value>
15611561
</data>
15621562
<data name="ID0446" xml:space="preserve">
1563-
<value>AS web authentication sessions are only supported on iOS 12.0+/macOS 10.15+/Mac Catalyst 12.0+ and require using an OS-specific target framework moniker (e.g on macOS, 'net8.0-macos15.0').</value>
1563+
<value>AS web authentication sessions are only supported on iOS 12.0+/macOS 10.15+/Mac Catalyst 12.0+ and require using an OS-specific target framework moniker on macOS (e.g 'net8.0-macos15.0').</value>
15641564
</data>
15651565
<data name="ID0447" xml:space="preserve">
15661566
<value>The current UI window cannot be resolved.</value>
@@ -1569,8 +1569,7 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId
15691569
<value>An unknown error occurred while trying to start an AS web authentication session.</value>
15701570
</data>
15711571
<data name="ID0449" xml:space="preserve">
1572-
<value>The generic version of the OpenIddict.Client.SystemIntegration package cannot be used on this platform. Make sure your application is referencing the correct version by using the appropriate OS-specific TFM (e.g on macOS, 'net8.0-macos10.15').</value>
1573-
<comment>This resource is no longer used and will be removed in a future version.</comment>
1572+
<value>The portable version of the OpenIddict.Client.SystemIntegration package cannot be used on Android, iOS and Mac Catalyst. Make sure your application is referencing the correct version by using the appropriate OS-specific TFM (e.g on iOS, 'net8.0-ios18.0').</value>
15741573
</data>
15751574
<data name="ID0450" xml:space="preserve">
15761575
<value>An HTTP redirect_uri or post_logout_redirect_uri cannot be used when using AS web authentication sessions. Make sure you're using a custom protocol scheme for all the callback URIs attached to the client registration. Alternatively, you can register an associated domain and use an HTTPS redirect_uri or post_logout_redirect_uri pointing to that domain (supported only on iOS 17.4+, Mac Catalyst 17.4+ and macOS 14.4+).</value>
@@ -1579,7 +1578,7 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId
15791578
<value>The Zoho integration requires sending the region of the server when using the client credentials or refresh token grants. For that, attach a ".location" authentication property containing the region to use.</value>
15801579
</data>
15811580
<data name="ID0452" xml:space="preserve">
1582-
<value>Custom tabs intents are only supported on Android and require using an OS-specific target framework moniker (e.g 'net8.0-android34.0').</value>
1581+
<value>Custom tabs intents are only supported on Android.</value>
15831582
</data>
15841583
<data name="ID0453" xml:space="preserve">
15851584
<value>The specified intent doesn't contain a valid data URI.</value>

src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationBuilder.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public OpenIddictClientSystemIntegrationBuilder UseSystemBrowser()
121121
/// <returns>The <see cref="OpenIddictClientSystemIntegrationBuilder"/>.</returns>
122122
public OpenIddictClientSystemIntegrationBuilder SetAllowedEmbeddedWebServerPorts(params int[] ports)
123123
{
124-
if (Array.Exists(ports, static port => port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort))
124+
if (Array.Exists(ports, static port => port is < IPEndPoint.MinPort or > IPEndPoint.MaxPort))
125125
{
126126
throw new ArgumentOutOfRangeException(nameof(ports));
127127
}
@@ -278,25 +278,15 @@ public OpenIddictClientSystemIntegrationBuilder SetPipeSecurity(PipeSecurity sec
278278
return Configure(options => options.PipeSecurity = security);
279279
}
280280

281-
/// <summary>
282-
/// Determines whether the specified object is equal to the current object.
283-
/// </summary>
284-
/// <param name="obj">The object to compare with the current object.</param>
285-
/// <returns><see langword="true"/> if the specified object is equal to the current object; otherwise, false.</returns>
281+
/// <inheritdoc/>
286282
[EditorBrowsable(EditorBrowsableState.Never)]
287283
public override bool Equals(object? obj) => base.Equals(obj);
288284

289-
/// <summary>
290-
/// Serves as the default hash function.
291-
/// </summary>
292-
/// <returns>A hash code for the current object.</returns>
285+
/// <inheritdoc/>
293286
[EditorBrowsable(EditorBrowsableState.Never)]
294287
public override int GetHashCode() => base.GetHashCode();
295288

296-
/// <summary>
297-
/// Returns a string that represents the current object.
298-
/// </summary>
299-
/// <returns>A string that represents the current object.</returns>
289+
/// <inheritdoc/>
300290
[EditorBrowsable(EditorBrowsableState.Never)]
301291
public override string? ToString() => base.ToString();
302292
}

src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationConfiguration.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,25 @@ public void PostConfigure(string? name, OpenIddictClientSystemIntegrationOptions
8383
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0389));
8484
}
8585

86+
#if !SUPPORTS_ANDROID
87+
// When running on Android, iOS or Mac Catalyst, ensure the version compiled for these platforms
88+
// is used to prevent the generic/non-OS specific TFM from being used as launching the system
89+
// browser cannot be done using Process.Start() and requires using OS-specific APIs that are
90+
// not available on the portable version of the OpenIddict.Client.SystemIntegration package.
91+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("android")))
92+
{
93+
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0449));
94+
}
95+
#endif
96+
97+
#if !SUPPORTS_UIKIT
98+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ios")) ||
99+
RuntimeInformation.IsOSPlatform(OSPlatform.Create("maccatalyst")))
100+
{
101+
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0449));
102+
}
103+
#endif
104+
86105
#if SUPPORTS_OPERATING_SYSTEM_VERSIONS_COMPARISON
87106
// Ensure the operating system version is supported.
88107
if ((OperatingSystem.IsAndroid() && !OperatingSystem.IsAndroidVersionAtLeast(21)) ||

src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ public static OpenIddictClientSystemIntegrationBuilder UseSystemIntegration(this
4242
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0389));
4343
}
4444

45+
#if !SUPPORTS_ANDROID
46+
// When running on Android, iOS or Mac Catalyst, ensure the version compiled for these platforms
47+
// is used to prevent the generic/non-OS specific TFM from being used as launching the system
48+
// browser cannot be done using Process.Start() and requires using OS-specific APIs that are
49+
// not available on the portable version of the OpenIddict.Client.SystemIntegration package.
50+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("android")))
51+
{
52+
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0449));
53+
}
54+
#endif
55+
56+
#if !SUPPORTS_UIKIT
57+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ios")) ||
58+
RuntimeInformation.IsOSPlatform(OSPlatform.Create("maccatalyst")))
59+
{
60+
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0449));
61+
}
62+
#endif
63+
4564
#if SUPPORTS_OPERATING_SYSTEM_VERSIONS_COMPARISON
4665
// Ensure the operating system version is supported.
4766
if ((OperatingSystem.IsAndroid() && !OperatingSystem.IsAndroidVersionAtLeast(21)) ||

src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.Authentication.cs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66

77
using System.Collections.Immutable;
8-
using System.Diagnostics;
98
using System.Runtime.InteropServices;
109
using System.Runtime.Versioning;
1110
using System.Text;
@@ -106,8 +105,6 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
106105
throw new ArgumentNullException(nameof(context));
107106
}
108107

109-
Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008));
110-
111108
#if SUPPORTS_AUTHENTICATION_SERVICES && SUPPORTS_FOUNDATION
112109
if (string.IsNullOrEmpty(context.RedirectUri) ||
113110
!Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri))
@@ -239,7 +236,7 @@ ASWebAuthenticationSession CreateASWebAuthenticationSession()
239236

240237
NSUrl CreateUrl() => new(OpenIddictHelpers.AddQueryStringParameters(
241238
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
242-
parameters: context.Transaction.Request.GetParameters().ToDictionary(
239+
parameters: context.Request.GetParameters().ToDictionary(
243240
static parameter => parameter.Key,
244241
static parameter => (StringValues) parameter.Value)).AbsoluteUri);
245242

@@ -331,8 +328,6 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
331328
throw new ArgumentNullException(nameof(context));
332329
}
333330

334-
Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008));
335-
336331
#if SUPPORTS_ANDROID && SUPPORTS_ANDROIDX_BROWSER
337332
if (string.IsNullOrEmpty(context.RedirectUri))
338333
{
@@ -359,7 +354,7 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
359354
// custom activity responsible for handling callback URIs pointing to a custom scheme.
360355
intent.LaunchUrl(Application.Context, NativeUri.Parse(OpenIddictHelpers.AddQueryStringParameters(
361356
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
362-
parameters: context.Transaction.Request.GetParameters().ToDictionary(
357+
parameters: context.Request.GetParameters().ToDictionary(
363358
static parameter => parameter.Key,
364359
static parameter => (StringValues) parameter.Value)).AbsoluteUri)!);
365360

@@ -404,8 +399,6 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
404399
throw new ArgumentNullException(nameof(context));
405400
}
406401

407-
Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008));
408-
409402
#if SUPPORTS_WINDOWS_RUNTIME
410403
if (string.IsNullOrEmpty(context.RedirectUri))
411404
{
@@ -444,7 +437,7 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
444437
options : WebAuthenticationOptions.None,
445438
requestUri : OpenIddictHelpers.AddQueryStringParameters(
446439
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
447-
parameters: context.Transaction.Request.GetParameters().ToDictionary(
440+
parameters: context.Request.GetParameters().ToDictionary(
448441
static parameter => parameter.Key,
449442
static parameter => (StringValues) parameter.Value)),
450443
callbackUri: new Uri(context.RedirectUri, UriKind.Absolute)))
@@ -551,11 +544,9 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
551544
throw new ArgumentNullException(nameof(context));
552545
}
553546

554-
Debug.Assert(context.Transaction.Request is not null, SR.GetResourceString(SR.ID4008));
555-
556547
var uri = OpenIddictHelpers.AddQueryStringParameters(
557548
uri: new Uri(context.AuthorizationEndpoint, UriKind.Absolute),
558-
parameters: context.Transaction.Request.GetParameters().ToDictionary(
549+
parameters: context.Request.GetParameters().ToDictionary(
559550
static parameter => parameter.Key,
560551
static parameter => (StringValues) parameter.Value));
561552

@@ -594,27 +585,35 @@ public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context)
594585
}
595586
}
596587

588+
// On Android, iOS and Mac Catalyst, Process.Start() is not supported and
589+
// OS-specific/non-portable APIs must be used to launch the system browser.
597590
#if SUPPORTS_ANDROID
598591
if (OperatingSystem.IsAndroid() && TryLaunchBrowserWithGenericIntent(uri))
599592
{
600593
context.HandleRequest();
601594
return;
602595
}
603596
#endif
604-
605597
#if SUPPORTS_UIKIT
606598
if ((OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst()) && await TryLaunchBrowserWithUIApplicationAsync(uri))
607599
{
608600
context.HandleRequest();
609601
return;
610602
}
611-
#elif SUPPORTS_APPKIT
603+
#endif
604+
#if SUPPORTS_APPKIT
612605
if (OperatingSystem.IsMacOS() && TryLaunchBrowserWithNSWorkspace(uri))
613606
{
614607
context.HandleRequest();
615608
return;
616609
}
617610
#endif
611+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && await TryLaunchBrowserWithOpenAsync(uri))
612+
{
613+
context.HandleRequest();
614+
return;
615+
}
616+
618617
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && await TryLaunchBrowserWithXdgOpenAsync(uri))
619618
{
620619
context.HandleRequest();
@@ -650,8 +649,6 @@ public async ValueTask HandleAsync(ApplyRedirectionResponseContext context)
650649
throw new ArgumentNullException(nameof(context));
651650
}
652651

653-
Debug.Assert(context.Transaction.Response is not null, SR.GetResourceString(SR.ID4007));
654-
655652
// This handler only applies to HTTP listener requests. If the HTTP context cannot be resolved,
656653
// this may indicate that the request was incorrectly processed by another server stack.
657654
var response = context.Transaction.GetHttpListenerContext()?.Response ??
@@ -663,7 +660,7 @@ public async ValueTask HandleAsync(ApplyRedirectionResponseContext context)
663660

664661
// Return a message indicating whether the authentication process
665662
// succeeded or failed and that will be visible by the user.
666-
var buffer = Encoding.UTF8.GetBytes(context.Transaction.Response.Error switch
663+
var buffer = Encoding.UTF8.GetBytes(context.Response.Error switch
667664
{
668665
null or { Length: 0 } => "Login completed. Please return to the application.",
669666
Errors.AccessDenied => "Authorization denied. Please return to the application.",

0 commit comments

Comments
 (0)