iOS: redirect after a failed authorize() crashes the app (finished session kept as _currentSession)
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- Nửa ngày
- Mức phù hợp với người mới
- 35/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- ios, objective-c, react-native, swift
- Lĩnh vực
- authentication, mobile
Hướng nghiên cứu
Start in packages/react-native-app-auth/ios/RNAppAuth.m: read authorizeWithConfiguration: where _currentSession is assigned, the completion block that clears it, and resumeExternalUserAgentFlowWithURL: near the top of the file. The author has already announced a fix and PR, so check for an open PR before starting. Done means a redirect that arrives after a failed authorize() no longer reaches a finished AppAuth session. This needs an iOS simulator with Xcode to verify.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Issue
When authorize() or logout() fails synchronously on iOS (the browser could not be opened), the native module keeps the failed AppAuth session as _currentSession. Any URL that later matches the redirect URL is forwarded to that finished session. With AppAuth 2.0.0 this terminates the app with an uncaught Objective-C exception:
An OAuth redirect was sent to a OIDExternalUserAgentSession after it already completed.
-[OIDAuthorizationSession resumeExternalUserAgentFlowWithURL:] (OIDAuthorizationService.m:135)
-[RNAppAuth resumeExternalUserAgentFlowWithURL:] (RNAppAuth.m:20)
AppDelegate.application(_:open:options:) (AppDelegate.swift:51)
It is an NSException, so a Swift AppDelegate cannot catch it.
This is the same exception and stack as #1080, with a different trigger. I have not reproduced the #1080 trigger myself.
Reproduction with examples/demo on the simulator
main at 93d06bc, AppAuth 2.0.0 as pinned by examples/demo/ios/Podfile.lock, Xcode 26.3, iPhone 17 Pro simulator on iOS 26.1.
- In
examples/demo/App.tsx, addiosCustomBrowser: 'chrome'to the options passed toauthorize(). - In
examples/demo/ios/Example/Info.plist, addgooglechromestoLSApplicationQueriesSchemes(AppAuth asserts on this in debug builds). - Run the app on a simulator, where Chrome is not installed, and tap Login with IdentityServer. An alert appears: "Failed to log in",
Unable to open Safari. - Dismiss the alert, then deliver a URL that matches the redirect URL:
xcrun simctl openurl booted "io.identityserver.demo:/oauthredirect?code=x&state=y"
- The app crashes with the stack above.
code and state are dummy values on purpose: the app crashes before either of them is read.
How I hit it in a production app
authorize() with iosCustomBrowser: 'safari', on an iPhone where Safari is disabled (Settings > Screen Time > Content & Privacy Restrictions > Allowed Apps) and Chrome is the default browser:
- Chrome opens with the login page, but the promise has already been rejected with
Unable to open Safari. - The user logs in and Chrome redirects to the app's custom scheme.
- The app is terminated with the exception above.
With Safari enabled the same flow works. Why AppAuth reports a failure although a browser did open is a separate problem in AppAuth's custom browser user-agent, which I will report to openid/AppAuth-iOS. Here it is only the trigger.
Cause
Links are to main at 93d06bc and to AppAuth 2.0.0.
authorizeWithConfiguration:stores the session returned by AppAuth (RNAppAuth.m#L441-L443).- AppAuth presents the session before returning it. If presenting fails, it completes the session and invokes the callback synchronously, before that call returns (
OIDAuthorizationService.m#L74-L86, throughOIDAuthState.m#L167-L169). - The callback clears
_currentSession(RNAppAuth.m#L426-L427). Then the call from step 1 returns, and the assignment puts the finished session back. - From then on, every URL that reaches the app delegate is forwarded to that session (
RNAppAuth.m#L19-L21). AppAuth accepts the URL if scheme, host and path match the redirect URL (the query is ignored), finds no pending callback and raises (OIDAuthorizationService.m#L123-L135).
The other authorize branches and logout follow the same pattern.
There is a second way to reach the same exception, which I found by reading the code and have not reproduced: _currentSession stays set until the code exchange finishes, while the AppAuth session is already complete once it has consumed the redirect. A second matching URL that arrives during the token request is forwarded to a finished session too.
AppAuth versions
AppAuth 2.1.0 replaced the exception with a NO return value (openid/AppAuth-iOS#955), so the crash needs AppAuth < 2.1.0. The podspec accepts AppAuth >= 1.7.6, and a project that already has 2.0.0 in its Podfile.lock keeps it until pod update. Holding on to a finished session looks wrong on any version, so I think it is worth fixing here as well.
I have a fix ready and will open a PR right after this issue.
Environment
- Your Identity Provider:
Keycloak(production app); the Duende demo server configured inexamples/demo(simulator reproduction) - Platform that you're experiencing the issue on:
iOS - Your
react-nativeVersion:0.82.1(production app, new architecture, Swift AppDelegate);0.79.2(examples/demo) - Your
react-native-app-authVersion:8.4.1(production app);mainat 93d06bc, i.e. 8.5.0 (examples/demo) - Are you using Expo? No
- AppAuth pod:
2.0.0in both - Device (production app): Apple iPhone 15 iOS: 27.0
- Ngôn ngữ chính
- Java
- Star
- 2.3k
- Fork
- 471
- Merge trung bình
- 4 ngày 3 giờ
- Pull request đã merge (30 ngày)
- 7
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của FormidableLabs/react-native-app-auth
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 20/100
-
[Android] AuthorizationService is never disposed: Custom Tabs bind leak breaks refresh() permanently after ~1000 callsCó thể đã có người làm @pranshuchittora đã nhận 82 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 62/100
-
Android: opt-in Partial Custom Tab (bottom-sheet) for the authorization flowCó thể đã có người làm @omeratt đã nhận 162 ngày trước. Đang mở
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
FormidableLabs/react-native-app-auth#1108 · 1 bình luận ·
Tất cả issue của FormidableLabs/react-native-app-auth
Issue tương tự
-
Documentation
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
OWASP/SecurityShepherd#1097 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Dans-Plugins/Medieval-Roleplay-Engine#392 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Invalid If-Modified-Since or If-Unmodified-Since header results in 500 with Request.evaluatePreconditions()Có thể đã có người làm @pschaub đã nhận hôm nay. Đang mởkind/bug triage/needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 70/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 62/100
Vakore/ZappierGames#236 ·