Skip to content

feat(ios): natively intercept https redirect URIs via ASWebAuthenticationSession callback (iOS 17.4+)#1122

Open
danchily2 wants to merge 6 commits into
FormidableLabs:mainfrom
danchily2:feat/ios-https-callback-aswebauthsession
Open

feat(ios): natively intercept https redirect URIs via ASWebAuthenticationSession callback (iOS 17.4+)#1122
danchily2 wants to merge 6 commits into
FormidableLabs:mainfrom
danchily2:feat/ios-https-callback-aswebauthsession

Conversation

@danchily2
Copy link
Copy Markdown

@danchily2 danchily2 commented Jun 5, 2026

Fixes #987

Description

When the redirect URI is an https universal link, OIDExternalUserAgentIOS creates the session with callbackURLScheme:@"https" — a scheme ASWebAuthenticationSession does 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 an authorize() 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 the callbackURLScheme: 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's OIDExternalUserAgentIOS, including ephemeral-session and presentation-context handling) and uses it automatically when:

  • no iosCustomBrowser is requested, and
  • the redirect URI scheme is https, and
  • running on iOS 17.4+.

Everything 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:

  • iOS-only by design: Android already supports https App Links redirects natively through AppAuth-Android's RedirectUriReceiverActivity intent filters, so there is no equivalent defect to replicate there.
  • No JS/TypeScript surface change → no index.js/index.spec.js/typings/readme updates needed; yarn test and yarn lint are unaffected (native-only change).
  • Changeset included (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-domains entitlement (webcredentials:example.com) and a webcredentials entry for the app in the domain's apple-app-site-association file. Without the association, the agent transparently falls back to the legacy callbackURLScheme session (AppAuth's default behavior), so apps without the association are unaffected.

  1. Configure an OIDC provider with an https universal-link redirect URI, add the webcredentials associated domain (entitlement + AASA), and run on an iOS 17.4+ device or simulator.
  2. Call authorize() and complete sign-in: the session intercepts the redirect itself — the sheet closes on the provider's 302 without loading the redirect page, and authorize() resolves with the token response.
  3. Cancel the sheet: authorize() rejects with the user-cancelled error as before.
  4. Remove the webcredentials association and repeat: the fallback path engages and behavior matches current main.
  5. Run on iOS < 17.4 (or with iosCustomBrowser, or a custom-scheme redirect URI): behavior is unchanged from current main.

🤖 Generated with Claude Code

…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>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 5, 2026

@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-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 5, 2026

🦋 Changeset detected

Latest commit: 44201c0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
react-native-app-auth Minor

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

danchily2 and others added 5 commits June 5, 2026 11:23
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Universal link not working as redirect URI

1 participant