From e2d9457dcc29afacf4f89c4055f98a02c595cd7b Mon Sep 17 00:00:00 2001 From: Federico Bartoli Date: Fri, 9 Oct 2026 17:41:51 +0200 Subject: [PATCH] Fix custom browser user-agent failing the flow when canOpenURL: returns NO --- CHANGELOG.md | 1 + .../OIDExternalUserAgentIOSCustomBrowser.m | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf49792c..50023e728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # UNRELEASED - Improved the state validation error messages to distinguish a missing `state` parameter from a mismatched one. The end session message now notes that a provider's `end_session_endpoint` may point at a legacy or SAML single-logout endpoint, which does not echo `state`. Addresses issue #956. +- Fixed `OIDExternalUserAgentIOSCustomBrowser` failing the flow with "Unable to open Safari." when `canOpenURL:` returns `NO` for a URL that the system can open, for example an https URL when Safari is restricted and another browser is the default. The flow now fails only if the URL really cannot be opened. Addresses issue #980. # 3.0.0 - BREAKING: Updates made to support Xcode 27. ([#972](https://github.com/openid/AppAuth-iOS/pull/972), [#973](https://github.com/openid/AppAuth-iOS/pull/973)) diff --git a/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m index 900bfbe19..784ada352 100644 --- a/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m +++ b/Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m @@ -27,6 +27,7 @@ #import "OIDAuthorizationRequest.h" #import "OIDAuthorizationService.h" #import "OIDErrorUtilities.h" +#import "OIDExternalUserAgentSession.h" #import "OIDURLQueryComponent.h" #if !TARGET_OS_MACCATALYST @@ -153,9 +154,24 @@ - (BOOL)presentExternalUserAgentRequest:(nonnull id // Transforms the request URL and opens it. NSURL *requestURL = [request externalUserAgentRequestURL]; requestURL = _URLTransformation(requestURL); - BOOL willOpen = [[UIApplication sharedApplication] canOpenURL:requestURL]; - [[UIApplication sharedApplication] openURL:requestURL options:@{} completionHandler:nil]; - return willOpen; + if (!requestURL) { + return NO; + } + // -canOpenURL: is not used to decide whether the request was presented: it is subject to + // LSApplicationQueriesSchemes and can return NO for a URL that + // -openURL:options:completionHandler: opens successfully (e.g. an https URL when Safari is + // unavailable and another browser is the default). The flow fails only if the open really fails. + [[UIApplication sharedApplication] openURL:requestURL + options:@{} + completionHandler:^(BOOL success) { + if (!success) { + NSError *safariError = [OIDErrorUtilities errorWithCode:OIDErrorCodeSafariOpenError + underlyingError:nil + description:@"Unable to open Safari."]; + [session failExternalUserAgentFlowWithError:safariError]; + } + }]; + return YES; } - (void)dismissExternalUserAgentAnimated:(BOOL)animated