Skip to content

Commit 502ddd2

Browse files
committed
Fix external Custom Tab auth callbacks
Avoid prompting on non-HTTP(s) callback intents when running as an external Custom Tab to prevent OAuth/app-link flows from looping back to the start. Also preserve original Custom Tabs extras when routing into CustomTabActivity and fix incorrect flag bitmasking.
1 parent 6ee77a3 commit 502ddd2

File tree

3 files changed

+78
-10
lines changed

3 files changed

+78
-10
lines changed

app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,13 +2229,25 @@ class BrowserTabFragment :
22292229
}
22302230

22312231
is Command.HandleNonHttpAppLink -> {
2232-
openExternalDialog(
2233-
intent = it.nonHttpAppLink.intent,
2234-
fallbackUrl = it.nonHttpAppLink.fallbackUrl,
2235-
fallbackIntent = it.nonHttpAppLink.fallbackIntent,
2236-
useFirstActivityFound = false,
2237-
headers = it.headers,
2238-
)
2232+
if (isActiveCustomTab() && isLaunchedFromExternalApp) {
2233+
// For external Custom Tabs, non-HTTP(s) schemes are frequently used as auth callbacks
2234+
// back into the originating app. Prompting here can break the flow and cause the app
2235+
// to restart/loop the login process.
2236+
launchNonHttpAppLinkFromExternalCustomTab(
2237+
intent = it.nonHttpAppLink.intent,
2238+
fallbackUrl = it.nonHttpAppLink.fallbackUrl,
2239+
fallbackIntent = it.nonHttpAppLink.fallbackIntent,
2240+
headers = it.headers,
2241+
)
2242+
} else {
2243+
openExternalDialog(
2244+
intent = it.nonHttpAppLink.intent,
2245+
fallbackUrl = it.nonHttpAppLink.fallbackUrl,
2246+
fallbackIntent = it.nonHttpAppLink.fallbackIntent,
2247+
useFirstActivityFound = false,
2248+
headers = it.headers,
2249+
)
2250+
}
22392251
}
22402252

22412253
is Command.ExtractUrlFromCloakedAmpLink -> {
@@ -2751,6 +2763,50 @@ class BrowserTabFragment :
27512763
}
27522764
}
27532765

2766+
private fun launchNonHttpAppLinkFromExternalCustomTab(
2767+
intent: Intent,
2768+
fallbackUrl: String? = null,
2769+
fallbackIntent: Intent? = null,
2770+
headers: Map<String, String> = emptyMap(),
2771+
) {
2772+
// Only act if the fragment is still active; avoids launching from background tabs.
2773+
if (!isActiveCustomTab()) return
2774+
2775+
context?.let { context ->
2776+
val pm = context.packageManager
2777+
val activities = pm.queryIntentActivities(intent, 0)
2778+
2779+
when {
2780+
activities.isEmpty() -> {
2781+
when {
2782+
fallbackIntent != null -> {
2783+
runCatching { context.startActivity(fallbackIntent) }
2784+
.onFailure { showToast(R.string.unableToOpenLink) }
2785+
}
2786+
2787+
fallbackUrl != null -> {
2788+
webView?.loadUrl(fallbackUrl, headers)
2789+
}
2790+
2791+
else -> showToast(R.string.unableToOpenLink)
2792+
}
2793+
}
2794+
2795+
activities.size == 1 -> {
2796+
runCatching { context.startActivity(intent) }
2797+
.onFailure { showToast(R.string.unableToOpenLink) }
2798+
}
2799+
2800+
else -> {
2801+
val title = getString(R.string.openExternalApp)
2802+
val chooser = Intent.createChooser(intent, title)
2803+
runCatching { context.startActivity(chooser) }
2804+
.onFailure { showToast(R.string.unableToOpenLink) }
2805+
}
2806+
}
2807+
}
2808+
}
2809+
27542810
private fun launchDialogForIntent(
27552811
context: Context,
27562812
pm: PackageManager,

app/src/main/java/com/duckduckgo/app/browser/customtabs/CustomTabActivity.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,19 @@ class CustomTabActivity : DuckDuckGoActivity() {
108108
}
109109

110110
companion object {
111-
fun intent(context: Context, flags: Int, text: String?, toolbarColor: Int, isExternal: Boolean): Intent {
112-
return Intent(context, CustomTabActivity::class.java).apply {
111+
fun intent(
112+
context: Context,
113+
originalIntent: Intent?,
114+
flags: Int,
115+
text: String?,
116+
toolbarColor: Int,
117+
isExternal: Boolean,
118+
): Intent {
119+
// Preserve Custom Tabs extras from the original caller intent (e.g., session binder),
120+
// while still routing to our CustomTabActivity implementation.
121+
val base = originalIntent?.let { Intent(it) } ?: Intent()
122+
return base.apply {
123+
setClass(context, CustomTabActivity::class.java)
113124
addFlags(flags)
114125
putExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, toolbarColor)
115126
putExtra(Intent.EXTRA_TEXT, text)

app/src/main/java/com/duckduckgo/app/dispatchers/IntentDispatcherActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ class IntentDispatcherActivity : DuckDuckGoActivity() {
7272
startActivity(
7373
CustomTabActivity.intent(
7474
context = this,
75-
flags = Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_CLEAR_TASK and Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
75+
originalIntent = intent,
76+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,
7677
text = intentText,
7778
toolbarColor = toolbarColor,
7879
isExternal = isExternal,

0 commit comments

Comments
 (0)