Custom browser user-agent fails the flow with "Unable to open Safari." while the browser opens (it returns the result of canOpenURL:)
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 25/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 停滞
- 技術スタック
- ios, objective-c
- 領域
- authentication, mobile
調査の方向性
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 の本文から書いたものです。
説明
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:
- Disable Safari: Settings > Screen Time > Content & Privacy Restrictions > Allowed Apps > Safari off.
- Start an authorization request with
[OIDExternalUserAgentIOSCustomBrowser CustomBrowserSafari]. - Chrome opens with the authorization page. At the same time the authorization callback is invoked with
OIDErrorCodeSafariOpenError, "Unable to open Safari." - Complete the sign-in in Chrome. The redirect reaches a session that has already completed. With AppAuth 2.0.0 this raises
OIDOAuthExceptionInvalidAuthorizationFlowand 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.mis unchanged in 2.1.0, 3.0.0 and onmaster.
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
- フォーク
- 866
- 平均マージ
- 9日 20時間
- マージ済み PR(30日)
- 1
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
openid/AppAuth-iOS のほかの issue
-
enhancement
難易度 1/5 1時間未満 初心者へのやさしさ 60/100
openid/AppAuth-iOS#908 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
openid/AppAuth-iOS#962 ·
-
enhancement triage
難易度 4/5 3〜5日 初心者へのやさしさ 38/100
openid/AppAuth-iOS#953 ·
-
enhancement triage
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
openid/AppAuth-iOS#948 ·
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 35/100
openid/AppAuth-iOS#932 · コメント 1 件 ·
openid/AppAuth-iOS の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
AvaloniaUI/Avalonia#22437 ·
メンテナーはふだん 1 日以内に返信
-
[Fix proposed] macOS 10.9: crash on startup due to +[NSColor linkColor] (10.10+ API) in menu_macos.mm対応中かも @maron2000 が今日担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 79/100
joncampbell123/dosbox-x#6664 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Typo in installer: “addition protections” → “additional protections”対応中かも @Jah-yee が 2 日前に担当しました。 オープン
難易度 1/5 1時間未満 初心者へのやさしさ 94/100
objective-see/BlockBlock#111 ·
-
s: need triage
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
Tencent-TDS/KuiklyUI#1786 ·
メンテナーはふだん 1 日以内に返信