feat(ios): natively intercept https redirect URIs via ASWebAuthenticationSession callback (iOS 17.4+)#1122
Conversation
…tionSession callback (iOS 17.4+) With an https (universal link) redirect URI, the session is created with callbackURLScheme:@"https", which ASWebAuthenticationSession does not support, so it never intercepts the redirect. Completion then depends on the universal link opening the app from inside the auth session - which is not triggered by server redirects or JS navigation and sporadically never happens, leaving authorize() pending forever with the user stuck on a disabled login UI (FormidableLabs#987, FormidableLabs#932; see also openid/AppAuth-iOS#367). On iOS 17.4+ Apple provides ASWebAuthenticationSessionCallback callbackWithHTTPSHost:path:, letting the session intercept the https redirect natively - no app-site-association dependency, no trampoline pages, no custom scheme. This adds an external user agent built on that API and uses it automatically when no iosCustomBrowser is requested and the redirect URI scheme is https. Behavior on iOS < 17.4 is unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
|
@danchily2 is attempting to deploy a commit to the formidable-labs Team on Vercel. A member of the Team first needs to authorize it. |
🦋 Changeset detectedLatest commit: 44201c0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
… 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>
…ror code
The missing-association failure is reported with the SAME error code as a
user cancellation (ASWebAuthenticationSessionErrorCodeCanceledLogin), so
the previous fallback check never engaged and every sign-in hard-failed
('Application ... is not associated with domain ... Using HTTPS callbacks
requires Associated Domains using the webcredentials service type').
The association failure carries an NSLocalizedFailureReason while genuine
user cancellations do not - use that to trigger the legacy-session
fallback. Verified on an iOS 26 simulator: with the association missing,
the fallback engages and sign-in completes; with a newer login attempt
started, stale resolutions are still discarded.
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds an iOS-only progressive enhancement to ensure authorize() can complete when the redirect URI is an https universal link on iOS 17.4+, by using the new ASWebAuthenticationSession HTTPS callback API so the auth session intercepts the redirect natively.
Changes:
- Introduces
RNAppAuthHTTPSExternalUserAgent, modeled after AppAuth-iOS’s iOS external user agent, usingASWebAuthenticationSessionCallback callbackWithHTTPSHost:path:on iOS 17.4+. - Automatically selects the new external user agent for
httpsredirect URIs on iOS 17.4+ wheniosCustomBrowseris not specified, with a legacy fallback path. - Adds a Changesets entry to publish the enhancement as a minor change.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| packages/react-native-app-auth/ios/RNAppAuth.m | Adds an iOS 17.4+ external user agent to intercept https redirect URIs inside ASWebAuthenticationSession, plus selection logic in authorize(). |
| .changeset/https-callback-ios.md | Declares a minor release describing the iOS 17.4+ HTTPS-callback enhancement. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| API_AVAILABLE(ios(17.4)) | ||
| @interface RNAppAuthHTTPSExternalUserAgent : NSObject <OIDExternalUserAgent, ASWebAuthenticationPresentationContextProviding> | ||
|
|
||
| - (nonnull instancetype)initWithPresentingViewController:(nonnull UIViewController *)presentingViewController | ||
| prefersEphemeralSession:(BOOL)prefersEphemeralSession | ||
| host:(nonnull NSString *)host | ||
| path:(nonnull NSString *)path; | ||
|
|
||
| @end |
| // Prefer the native https-callback session for https (universal link) redirect URIs | ||
| // (see RNAppAuthHTTPSExternalUserAgent above). Falls back to default behavior pre-17.4. | ||
| if (externalUserAgent == nil) { | ||
| if (@available(iOS 17.4, *)) { | ||
| NSURL *httpsRedirectURL = [NSURL URLWithString:redirectUrl]; | ||
| if ([httpsRedirectURL.scheme isEqualToString:@"https"] && httpsRedirectURL.host != nil) { | ||
| externalUserAgent = [[RNAppAuthHTTPSExternalUserAgent alloc] | ||
| initWithPresentingViewController:presentingViewController | ||
| prefersEphemeralSession:prefersEphemeralSession | ||
| host:httpsRedirectURL.host | ||
| path:httpsRedirectURL.path.length > 0 ? httpsRedirectURL.path : @"/"]; | ||
| } | ||
| } | ||
| } |
| /** | ||
| * Implementation modeled on AppAuth's OIDExternalUserAgentIOS, but built | ||
| * with [ASWebAuthenticationSessionCallback callbackWithHTTPSHost:path:] so the session | ||
| * intercepts the https redirect itself (iOS 17.4+). | ||
| */ | ||
| @implementation RNAppAuthHTTPSExternalUserAgent { |
| return _presentingViewController.view.window; | ||
| } | ||
|
|
||
| @end |
Fixes #987
Description
When the redirect URI is an https universal link,
OIDExternalUserAgentIOScreates the session withcallbackURLScheme:@"https"— a schemeASWebAuthenticationSessiondoes not support — so the session never intercepts the redirect. Flow completion then depends on the universal link opening the app from inside the auth session, which is not triggered by server-side redirects or JS navigation, and is sporadically dropped entirely. The result is anauthorize()promise that never settles, with the user stuck on a permanently disabled login UI (also reported as #932; root cause tracked upstream since 2018 in openid/AppAuth-iOS#367, and thecallbackURLScheme:initializer is deprecated per openid/AppAuth-iOS#847). The same failure class affects other SDKs (e.g. google/GoogleSignIn-iOS#388). The common workaround — a hosted "trampoline" page forwarding the callback to a custom scheme — reintroduces the custom scheme universal links were adopted to avoid, and the in-session JS hop is itself sporadically blocked by WebKit.iOS 17.4 added
ASWebAuthenticationSessionCallback callbackWithHTTPSHost:path:, which lets the session intercept an https redirect natively — no app-site-association timing dependency, no trampoline page, no custom scheme.This PR adds
RNAppAuthHTTPSExternalUserAgent(modeled on AppAuth'sOIDExternalUserAgentIOS, including ephemeral-session and presentation-context handling) and uses it automatically when:iosCustomBrowseris requested, andhttps, andEverything else — custom browsers, custom-scheme redirect URIs, iOS < 17.4 — keeps the existing behavior, so this is a progressive enhancement with no API surface change.
Notes per CONTRIBUTING:
RedirectUriReceiverActivityintent filters, so there is no equivalent defect to replicate there.index.js/index.spec.js/typings/readme updates needed;yarn testandyarn lintare unaffected (native-only change).minor).Steps to verify
Requirement: the https callback path needs the callback host registered as an associated domain with the webcredentials service type — both the
com.apple.developer.associated-domainsentitlement (webcredentials:example.com) and awebcredentialsentry for the app in the domain'sapple-app-site-associationfile. Without the association, the agent transparently falls back to the legacycallbackURLSchemesession (AppAuth's default behavior), so apps without the association are unaffected.webcredentialsassociated domain (entitlement + AASA), and run on an iOS 17.4+ device or simulator.authorize()and complete sign-in: the session intercepts the redirect itself — the sheet closes on the provider's 302 without loading the redirect page, andauthorize()resolves with the token response.authorize()rejects with the user-cancelled error as before.webcredentialsassociation and repeat: the fallback path engages and behavior matches currentmain.iosCustomBrowser, or a custom-scheme redirect URI): behavior is unchanged from currentmain.🤖 Generated with Claude Code