Skip to content

Commit 1d997c5

Browse files
danchily2claude
andcommitted
fix(ios): fall back to legacy session when webcredentials association is missing
The https callback requires the callback host to be an associated domain with the webcredentials service type. Without it the session refuses to start (start returns NO, or the completion handler fires immediately with a non-cancel error), which would hard-fail every sign-in. Detect both cases and transparently fall back to the legacy callbackURLScheme session, preserving AppAuth's default behavior for apps without the association. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d7e51af commit 1d997c5

1 file changed

Lines changed: 66 additions & 19 deletions

File tree

packages/react-native-app-auth/ios/RNAppAuth.m

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
* not triggered by server redirects/JS navigation inside the session and is sporadically
1919
* dropped, leaving authorize() pending forever (#987, #932; openid/AppAuth-iOS#367).
2020
* This agent lets the session intercept the https redirect natively.
21+
*
22+
* Requires the callback host to be an associated domain with the webcredentials service
23+
* type (entitlement + apple-app-site-association). When the association is missing the
24+
* agent transparently falls back to the legacy callbackURLScheme session, preserving
25+
* AppAuth's default behavior.
2126
*/
2227
API_AVAILABLE(ios(17.4))
2328
@interface RNAppAuthHTTPSExternalUserAgent : NSObject <OIDExternalUserAgent, ASWebAuthenticationPresentationContextProviding>
@@ -850,8 +855,10 @@ @implementation RNAppAuthHTTPSExternalUserAgent {
850855
NSString *_host;
851856
NSString *_path;
852857
BOOL _externalUserAgentFlowInProgress;
858+
BOOL _didFallBackToLegacySession;
853859
__weak id<OIDExternalUserAgentSession> _session;
854860
ASWebAuthenticationSession *_webAuthenticationSession;
861+
NSURL *_requestURL;
855862
}
856863

857864
- (instancetype)initWithPresentingViewController:(UIViewController *)presentingViewController
@@ -875,36 +882,76 @@ - (BOOL)presentExternalUserAgentRequest:(id<OIDExternalUserAgentRequest>)request
875882
}
876883
_externalUserAgentFlowInProgress = YES;
877884
_session = session;
885+
_requestURL = [request externalUserAgentRequestURL];
878886

879-
NSURL *requestURL = [request externalUserAgentRequestURL];
880-
__weak typeof(self) weakSelf = self;
887+
ASWebAuthenticationSession *webAuthenticationSession = [self authenticationSessionWithHTTPSCallback:YES];
888+
_webAuthenticationSession = webAuthenticationSession;
889+
if ([webAuthenticationSession start]) {
890+
return YES;
891+
}
892+
return [self startLegacyFallbackSession];
893+
}
894+
895+
/**
896+
* The https callback requires the callback host to be an associated domain with the
897+
* webcredentials service type (entitlement + apple-app-site-association entry). When the
898+
* association is missing or not yet validated, the session refuses to start — either
899+
* start returns NO or the completion handler fires immediately with a non-cancel error.
900+
* In both cases fall back to the legacy callbackURLScheme session, which matches
901+
* AppAuth's default behavior, so sign-in keeps working instead of hard-failing.
902+
*/
903+
- (BOOL)startLegacyFallbackSession {
904+
if (_didFallBackToLegacySession) {
905+
return NO;
906+
}
907+
_didFallBackToLegacySession = YES;
908+
ASWebAuthenticationSession *fallbackSession = [self authenticationSessionWithHTTPSCallback:NO];
909+
_webAuthenticationSession = fallbackSession;
910+
return [fallbackSession start];
911+
}
881912

882-
ASWebAuthenticationSessionCallback *callback =
883-
[ASWebAuthenticationSessionCallback callbackWithHTTPSHost:_host path:_path];
884-
ASWebAuthenticationSession *webAuthenticationSession =
885-
[[ASWebAuthenticationSession alloc] initWithURL:requestURL
886-
callback:callback
887-
completionHandler:^(NSURL *_Nullable callbackURL, NSError *_Nullable error) {
913+
- (ASWebAuthenticationSession *)authenticationSessionWithHTTPSCallback:(BOOL)useHTTPSCallback {
914+
__weak typeof(self) weakSelf = self;
915+
void (^completionHandler)(NSURL *_Nullable, NSError *_Nullable) =
916+
^(NSURL *_Nullable callbackURL, NSError *_Nullable error) {
888917
typeof(self) strongSelf = weakSelf;
889918
if (!strongSelf) {
890919
return;
891920
}
892921
strongSelf->_webAuthenticationSession = nil;
893922
if (callbackURL) {
894923
[strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL];
895-
} else {
896-
NSError *safariError =
897-
[OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
898-
underlyingError:error
899-
description:nil];
900-
[strongSelf->_session failExternalUserAgentFlowWithError:safariError];
924+
return;
901925
}
902-
}];
926+
BOOL isUserCancel = [error.domain isEqualToString:ASWebAuthenticationSessionErrorDomain] &&
927+
error.code == ASWebAuthenticationSessionErrorCodeCanceledLogin;
928+
if (useHTTPSCallback && !isUserCancel && [strongSelf startLegacyFallbackSession]) {
929+
// Missing/unvalidated webcredentials association — legacy session took over
930+
return;
931+
}
932+
NSError *safariError =
933+
[OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
934+
underlyingError:error
935+
description:nil];
936+
[strongSelf->_session failExternalUserAgentFlowWithError:safariError];
937+
};
903938

904-
webAuthenticationSession.presentationContextProvider = self;
905-
webAuthenticationSession.prefersEphemeralWebBrowserSession = _prefersEphemeralSession;
906-
_webAuthenticationSession = webAuthenticationSession;
907-
return [webAuthenticationSession start];
939+
ASWebAuthenticationSession *session;
940+
if (useHTTPSCallback) {
941+
ASWebAuthenticationSessionCallback *callback =
942+
[ASWebAuthenticationSessionCallback callbackWithHTTPSHost:_host path:_path];
943+
session = [[ASWebAuthenticationSession alloc] initWithURL:_requestURL
944+
callback:callback
945+
completionHandler:completionHandler];
946+
} else {
947+
// Matches AppAuth's OIDExternalUserAgentIOS default for https redirect URIs
948+
session = [[ASWebAuthenticationSession alloc] initWithURL:_requestURL
949+
callbackURLScheme:@"https"
950+
completionHandler:completionHandler];
951+
}
952+
session.presentationContextProvider = self;
953+
session.prefersEphemeralWebBrowserSession = _prefersEphemeralSession;
954+
return session;
908955
}
909956

910957
- (void)dismissExternalUserAgentAnimated:(BOOL)animated completion:(nonnull void (^)(void))completion {

0 commit comments

Comments
 (0)