Skip to content

Add preventUniversalLinks prop to sandbox hosts from UL handoff#10

Merged
artemlitch merged 1 commit intomasterfrom
artem/prevent-universal-links-v2
May 7, 2026
Merged

Add preventUniversalLinks prop to sandbox hosts from UL handoff#10
artemlitch merged 1 commit intomasterfrom
artem/prevent-universal-links-v2

Conversation

@artemlitch
Copy link
Copy Markdown
Collaborator

Summary

Adds a preventUniversalLinks prop (iOS) that lists hosts whose top-frame navigations should be sandboxed from Universal Link handoff. For listed hosts, the navigation is canceled in decidePolicyForNavigationAction and re-issued via [webView loadRequest:] — the only nav type iOS reliably does not consider for UL handoff.

Why

When iOS triggers a Universal Link handoff mid-flow inside a WebView, the user is yanked into the linked third-party app and the embedded flow breaks. Most commonly hit in embedded auth: e.g. a Goodreads sign-in WebView whose post-Amazon-SSO redirect lands on a UL-eligible URL like /ap-handler/sign-in?IDP=lwa, sending the user into the installed Goodreads app.

iOS does not expose a way to predict UL handoff at the navigation delegate — UL is decided in WebKit's process before decidePolicyForNavigationAction: fires.

API

<WebView preventUniversalLinks={['goodreads.com']} ... />
  • Match is domain-suffix with a dot boundary: 'goodreads.com' covers goodreads.com, www.goodreads.com, and any subdomain — but not evilgoodreads.com.
  • Takes a host list rather than a boolean because reissuing every top-frame nav unconditionally breaks anything that's not a plain GET — POST form bodies are stripped by NSURLRequest in the navigation action, and reissuing single-use OAuth codes (Amazon callbacks etc.) double-consumes them.
  • An associated-object flag on the WKWebView instance breaks the cancel/reissue loop.

Files

  • apple/RNCWebViewImpl.{h,m} — cancel/reissue logic with loop guard
  • apple/RNCWebView.mm — Fabric prop array conversion (mirrors suppressMenuItems pattern)
  • apple/RNCWebViewManager.mm — Paper prop registration
  • src/RNCWebViewNativeComponent.ts, src/WebViewTypes.ts — TS spec + JSDoc
  • lib/* — rebuilt to match

Test plan

Verified end-to-end on a physical iPhone via the Bookwise consumer (readwiseio/rekindled#9205):

  • Fresh sign-in via Amazon SSO → import completes
  • Rescan after killing & reopening the app → import completes
  • WebView never hands off to the Goodreads app

🤖 Generated with Claude Code

When iOS triggers a Universal Link handoff mid-flow inside a WebView, the
user is yanked into the linked third-party app and the embedded flow
breaks. This shows up most commonly in embedded auth: e.g. a Goodreads
sign-in WebView whose post-Amazon-SSO redirect lands on a UL-eligible
URL like /ap-handler/sign-in?IDP=lwa, sending the user into the
installed Goodreads app.

iOS does not expose a way to predict UL handoff at the navigation
delegate — UL is decided in WebKit's process before
decidePolicyForNavigationAction fires. The only nav type iOS reliably
does NOT consider for UL handoff is host-app-initiated
[webView loadRequest:].

This adds a preventUniversalLinks prop (NSArray<NSString *>) for iOS:
when set, top-frame navigations whose host matches an entry are
canceled and re-issued via [webView loadRequest:]. Match is
domain-suffix with a dot boundary, so passing 'goodreads.com' covers
'goodreads.com', 'www.goodreads.com', and any subdomain — but not
'evilgoodreads.com'.

The prop takes a host list rather than a boolean because reissuing
every top-frame nav unconditionally breaks anything that's not a plain
GET — POST form bodies are stripped by NSURLRequest in the navigation
action, and reissuing single-use OAuth codes (Amazon callbacks etc.)
double-consumes them. Only hosts that actually have AASA registered
for this app's bundle ID are at risk of UL handoff, so the app
declares them.

An associated-object flag on the WKWebView breaks the cancel/reissue
loop: the reissued nav re-enters decidePolicyForNavigationAction with
the flag set, the impl clears it and falls through to the normal
handler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit 7fbf264)
@artemlitch artemlitch merged commit a827dc4 into master May 7, 2026
1 of 9 checks passed
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.

1 participant