Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto
#980 0 comentarios 0 reacciones 0 asignados Ver en GitHub

@federicobartoli ya está trabajando en esto.

Desde el 9/10/2026.

  • #981 de @federicobartoli — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
25/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Estancado
Stack tecnológico
ios, objective-c

Línea de trabajo

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.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
Objective-C
Estrellas
2k
Forks
866
Merge medio
9 d 20 h
PR fusionados (30 d)
1

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de openid/AppAuth-iOS

Todos los issues de openid/AppAuth-iOS

Issues similares

Más issues de Objective-C

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.