Summary
StripeConnectRedirectActivity.customTabIntent(...) launches the Custom Tab via CustomTabsClient.getPackageName(this, null) (no browser preference list) and CustomTabsIntent.Builder().build() (no setShareState call). For users whose system default Android browser is Firefox, this surfaces a stale Firefox tab — typically the last URL Firefox had open from any previous session — instead of the URL passed via EXTRA_CUSTOM_TAB_URL. The symptom persists across phone reboots (Firefox restores its session aggressively), and repeated taps stack into multiple Firefox tasks rather than rendering the requested URL.
Switching the default browser to Chrome on the same device resolves the symptom entirely — confirming this only reproduces when Firefox is the system default browser supporting the Custom Tabs API.
Repro
- Set the device's default browser to Firefox.
- With Firefox, navigate to any web page (this becomes Firefox's restorable session).
- Optionally reboot the device.
- In a host app integrating Connect Embedded, open
account-onboarding via EmbeddedComponentManager.createAccountOnboardingController(activity).show().
- Inside the embedded WebView, tap any UI control that resolves to an
http(s) URL outside ALLOWLISTED_HOSTS (so StripeConnectWebViewContainerViewModel.shouldOverrideUrlLoading routes to launchSecureExternalWebTab).
Expected: Firefox opens to the URL passed to StripeConnectRedirectActivity.EXTRA_CUSTOM_TAB_URL.
Observed: Firefox surfaces a previously-open tab. The URL bar shows that older URL, not the one Stripe just passed. Repeated taps in step 5 stack multiple Firefox tasks, each requiring its own back-press to dismiss.
Root cause (best guess)
From connect/src/main/java/com/stripe/android/connect/StripeConnectRedirectActivity.kt on master:
val customTabsIntent = CustomTabsIntent.Builder().build()
CustomTabsClient.getPackageName(this, null)?.let { browserPackage ->
customTabsIntent.intent.setPackage(browserPackage)
}
customTabsIntent.launchUrl(this, Uri.parse(customTabUrl))
CustomTabsClient.getPackageName(this, null) returns whatever browser is the system default that supports Custom Tabs — Firefox in this case.
CustomTabsIntent.Builder().build() uses default shareState (SHARE_STATE_DEFAULT), which permits the launching app's browsing state (cookies, history, restored tabs) to be shared with the Custom Tab.
Firefox for Android's Custom Tabs implementation diverges from Chrome's in how aggressively it shares the Custom Tab with the main browser session. Under SHARE_STATE_DEFAULT, Firefox effectively renders the Custom Tab from its restored browser state rather than freshly fetching the URL passed in the intent.
Suggested fixes (either resolves it; both catches Firefox-only users as well)
(a) Prefer Chrome-family browsers when selecting the Custom Tab provider — pass an explicit package list to CustomTabsClient.getPackageName(...):
val preferredPackages = listOf(
"com.android.chrome",
"com.chrome.beta",
"com.brave.browser",
"com.duckduckgo.mobile.android",
// …
)
val browserPackage = CustomTabsClient.getPackageName(this, preferredPackages)
This selects from the preferred list when any are installed, falling back to the system default only when none are. Skips Firefox if Chrome (or another known-good browser) is available.
(b) Set setShareState(CustomTabsIntent.SHARE_STATE_OFF) on the builder:
val customTabsIntent = CustomTabsIntent.Builder()
.setShareState(CustomTabsIntent.SHARE_STATE_OFF)
.build()
This is the documented mechanism for requesting an isolated Custom Tab independent of the browser's broader session. Even where some browsers honor it imperfectly, setting it makes intent explicit and shifts conformance to the browser implementation.
Why this matters
For apps integrating Connect Embedded, users running Firefox as their default browser may have trouble to reach Stripe's documentation / support / onboarding-companion pages via the standard external-link path. They instead see whatever Firefox tab was last open — including potentially sensitive prior browsing. "Switch your default browser" is a non-obvious workaround we wouldn't expect end users to discover.
Environment
- Affected SDK version:
com.stripe:connect:23.10.0 — the unchanged code is identical on master at the time of writing.
- Repro device: Samsung Galaxy S23 FE (SM-S916U), Android 16, Firefox for Android set as default browser.
- Verified resolution: switching the default browser to Chrome on the same device.
Summary
StripeConnectRedirectActivity.customTabIntent(...)launches the Custom Tab viaCustomTabsClient.getPackageName(this, null)(no browser preference list) andCustomTabsIntent.Builder().build()(nosetShareStatecall). For users whose system default Android browser is Firefox, this surfaces a stale Firefox tab — typically the last URL Firefox had open from any previous session — instead of the URL passed viaEXTRA_CUSTOM_TAB_URL. The symptom persists across phone reboots (Firefox restores its session aggressively), and repeated taps stack into multiple Firefox tasks rather than rendering the requested URL.Switching the default browser to Chrome on the same device resolves the symptom entirely — confirming this only reproduces when Firefox is the system default browser supporting the Custom Tabs API.
Repro
account-onboardingviaEmbeddedComponentManager.createAccountOnboardingController(activity).show().http(s)URL outsideALLOWLISTED_HOSTS(soStripeConnectWebViewContainerViewModel.shouldOverrideUrlLoadingroutes tolaunchSecureExternalWebTab).Expected: Firefox opens to the URL passed to
StripeConnectRedirectActivity.EXTRA_CUSTOM_TAB_URL.Observed: Firefox surfaces a previously-open tab. The URL bar shows that older URL, not the one Stripe just passed. Repeated taps in step 5 stack multiple Firefox tasks, each requiring its own back-press to dismiss.
Root cause (best guess)
From
connect/src/main/java/com/stripe/android/connect/StripeConnectRedirectActivity.ktonmaster:CustomTabsClient.getPackageName(this, null)returns whatever browser is the system default that supports Custom Tabs — Firefox in this case.CustomTabsIntent.Builder().build()uses defaultshareState(SHARE_STATE_DEFAULT), which permits the launching app's browsing state (cookies, history, restored tabs) to be shared with the Custom Tab.Firefox for Android's Custom Tabs implementation diverges from Chrome's in how aggressively it shares the Custom Tab with the main browser session. Under
SHARE_STATE_DEFAULT, Firefox effectively renders the Custom Tab from its restored browser state rather than freshly fetching the URL passed in the intent.Suggested fixes (either resolves it; both catches Firefox-only users as well)
(a) Prefer Chrome-family browsers when selecting the Custom Tab provider — pass an explicit package list to
CustomTabsClient.getPackageName(...):This selects from the preferred list when any are installed, falling back to the system default only when none are. Skips Firefox if Chrome (or another known-good browser) is available.
(b) Set
setShareState(CustomTabsIntent.SHARE_STATE_OFF)on the builder:This is the documented mechanism for requesting an isolated Custom Tab independent of the browser's broader session. Even where some browsers honor it imperfectly, setting it makes intent explicit and shifts conformance to the browser implementation.
Why this matters
For apps integrating Connect Embedded, users running Firefox as their default browser may have trouble to reach Stripe's documentation / support / onboarding-companion pages via the standard external-link path. They instead see whatever Firefox tab was last open — including potentially sensitive prior browsing. "Switch your default browser" is a non-obvious workaround we wouldn't expect end users to discover.
Environment
com.stripe:connect:23.10.0— the unchanged code is identical onmasterat the time of writing.