Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Custom browser user-agent fails the flow with "Unable to open Safari." while the browser opens (it returns the result of canOpenURL:)

未关闭
#980 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

@federicobartoli 已经在做这个了。

开始于 2026年10月9日。

  • #981 来自 @federicobartoli —— 未关闭

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
25/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
停滞
技术栈
ios, objective-c

调研方向

The change belongs in presentExternalUserAgentRequest:session: in Sources/AppAuth/iOS/OIDExternalUserAgentIOSCustomBrowser.m, which currently returns the result of canOpenURL:. Sources/AppAuthCore/OIDAuthorizationService.m shows where the session fails with OIDErrorCodeSafariOpenError. Linked PR #981 is already open against this issue, so review it before starting. Done means the method reports presentation as started and fails the session only when the open completion handler reports failure.

由索引模型根据 Issue 内容生成。

描述

bug triage

Describe the bug

-[OIDExternalUserAgentIOSCustomBrowser presentExternalUserAgentRequest:session:] returns the result of canOpenURL: but calls openURL:options:completionHandler: regardless of it (OIDExternalUserAgentIOSCustomBrowser.m#L153-L158).

When canOpenURL: answers NO for a URL that the system can in fact open, the browser opens and the user can sign in, but the session has already been failed with OIDErrorCodeSafariOpenError, "Unable to open Safari." (OIDAuthorizationService.m#L78-L85). The redirect that comes back afterwards has no pending flow to resume, so the sign-in cannot complete.

To Reproduce

On an iPhone with Chrome installed and set as the default browser, in an app that does not list https under LSApplicationQueriesSchemes:

  1. Disable Safari: Settings > Screen Time > Content & Privacy Restrictions > Allowed Apps > Safari off.
  2. Start an authorization request with [OIDExternalUserAgentIOSCustomBrowser CustomBrowserSafari].
  3. Chrome opens with the authorization page. At the same time the authorization callback is invoked with OIDErrorCodeSafariOpenError, "Unable to open Safari."
  4. Complete the sign-in in Chrome. The redirect reaches a session that has already completed. With AppAuth 2.0.0 this raises OIDOAuthExceptionInvalidAuthorizationFlow and the app is terminated.

What I measured on the device, for the provider's https URL:

Safari https in LSApplicationQueriesSchemes canOpenURL: Sign-in
enabled no YES works
disabled no NO fails as described, although Chrome opens
disabled yes YES works

The canOpenURL: values were read through React Native's Linking.canOpenURL, which calls -[UIApplication canOpenURL:].

Expected behavior

The request is reported as presented, because the browser did open. The session stays pending until the redirect arrives, and fails only if the URL really could not be opened.

Screenshots

Not applicable.

Environment

  • Device: iPhone 15
  • OS: iOS 27.0.1
  • Browser: Chrome as the default browser, Safari disabled through Screen Time
  • AppAuth 2.0.0 via CocoaPods, used through react-native-app-auth 8.4.1. OIDExternalUserAgentIOSCustomBrowser.m is unchanged in 2.1.0, 3.0.0 and on master.

Additional context

Cause. canOpenURL: is a prediction with its own preconditions. Apple's documentation says it returns false "if you haven't declared the URL's scheme in your Info.plist file", and that open(_:options:completionHandler:) "isn't constrained by the LSApplicationQueriesSchemes requirement. If an app is available to handle the URL, the system will launch it, even if you haven't declared the scheme." A true result guarantees that the open will succeed; a false result does not mean it will fail. I reported the inconsistent answer for https to Apple as FB25124433.

History. Until 1.7.5 this method returned the result of the synchronous -[UIApplication openURL:], that is, the real outcome. #871 (released in 1.7.6) replaced the deprecated call and returned the result of canOpenURL: instead, and #911 kept that.

Later versions. #955 (2.1.0) replaced the exception with an error return, so step 4 no longer crashes there. I have only run 2.0.0, but from the code the session is failed in the same way on 2.1.0 and 3.0.0, so the sign-in still cannot complete.

Proposed fix. Report the presentation as started and fail the session only if the open really fails, using the completion handler. The error code and message stay the same.

--- 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 @@ NS_ASSUME_NONNULL_BEGIN
   // 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;
 }

I tested this change on the device above, applied to AppAuth 2.0.0, with Safari disabled and without the https declaration, so canOpenURL: still returned NO: Chrome opens, the session stays pending and the sign-in completes.

The only observable change for a real failure is timing: the same error is delivered from the completion handler instead of synchronously through the NO return value. The "browser not installed, open the App Store" branch is untouched.

Workaround for integrators. Adding https to LSApplicationQueriesSchemes makes canOpenURL: return YES in this situation.

I am aware the README describes this user-agent as not intended for consumer apps. The change restores what the method reported before 1.7.6, and apps that have to return to the same browser after a hand-off to a separate authenticator app have no other built-in user-agent to use. I'm happy to open a PR for it.

Downstream report: FormidableLabs/react-native-app-auth#1151.

主要语言
Objective-C
星标
2k
派生
864
平均合并
1 天 10 小时
30 天内合并 PR
1

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

openid/AppAuth-iOS 的其他 Issue

查看 openid/AppAuth-iOS 的全部 Issue

相似的 Issue

更多 Objective-C Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。