Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Browser
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsService
import androidx.core.net.toUri
import com.okta.authfoundation.events.EventCoordinator
import com.okta.webauthenticationui.events.CustomizeBrowserEvent
import com.okta.webauthenticationui.events.CustomizeCustomTabsEvent
Expand Down Expand Up @@ -56,6 +56,8 @@ internal class DefaultWebAuthenticationProvider(
const val X_OKTA_USER_AGENT = "X-Okta-User-Agent-Extended"

val USER_AGENT_HEADER = "web-authentication-ui/${Build.VERSION.SDK_INT} com.okta.webauthenticationui/2.0.0"

val HTTP_URI_FOR_BROWSER_VALIDATION = "http://".toUri()
}

override fun launch(
Expand All @@ -76,7 +78,7 @@ internal class DefaultWebAuthenticationProvider(
tabsIntent.intent.putExtra(Browser.EXTRA_HEADERS, headers)

try {
tabsIntent.launchUrl(context, Uri.parse(url.toString()))
tabsIntent.launchUrl(context, url.toString().toUri())
return null
} catch (e: ActivityNotFoundException) {
return e
Expand All @@ -88,20 +90,29 @@ internal class DefaultWebAuthenticationProvider(
eventCoordinator.sendEvent(event)

val pm: PackageManager = context.packageManager
val serviceIntent = Intent()
serviceIntent.action = CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
val serviceIntent = Intent(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION)
val resolveInfoList = pm.queryIntentServices(serviceIntent, event.queryIntentServicesFlags)
val customTabsBrowsersPackages = mutableSetOf<String>()
for (info in resolveInfoList) {
customTabsBrowsersPackages.add(info.serviceInfo.packageName)

val customTabsBrowsersPackages = resolveInfoList
.mapNotNull { it.serviceInfo?.packageName }
.toSet()

val preferredBrowser = event.preferredBrowsers.firstOrNull { customTabsBrowsersPackages.contains(it) }
if (preferredBrowser != null && isBrowserPackage(pm, preferredBrowser)) {
return preferredBrowser
}

for (browser in event.preferredBrowsers) {
if (customTabsBrowsersPackages.contains(browser)) {
return browser
}
return customTabsBrowsersPackages.firstOrNull {
isBrowserPackage(pm, it)
}
}

return customTabsBrowsersPackages.firstOrNull()
private fun isBrowserPackage(pm: PackageManager, packageName: String): Boolean {
val intent = Intent(Intent.ACTION_VIEW, HTTP_URI_FOR_BROWSER_VALIDATION).apply {
addCategory(Intent.CATEGORY_BROWSABLE)
setPackage(packageName)
}
val resolveInfoList = pm.queryIntentActivities(intent, 0)
return resolveInfoList.isNotEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ import org.robolectric.shadows.ShadowResolveInfo

@RunWith(RobolectricTestRunner::class)
class DefaultWebAuthenticationProviderTest {
@Test fun testLaunch() {
@Test
fun testLaunch() {
val activity = Robolectric.buildActivity(Activity::class.java)
val webAuthenticationProvider = DefaultWebAuthenticationProvider(EventCoordinator(emptyList()))
assertThat(webAuthenticationProvider.launch(activity.get(), "https://example.com/not_used".toHttpUrl())).isNull()
Expand All @@ -54,7 +55,8 @@ class DefaultWebAuthenticationProviderTest {
assertThat(cctActivity.`package`).isNull()
}

@Test fun testLaunchCallsEventHandler() {
@Test
fun testLaunchCallsEventHandler() {
val activity = Robolectric.buildActivity(Activity::class.java)
val eventHandler = RecordingEventHandler()
val webAuthenticationProvider = DefaultWebAuthenticationProvider(EventCoordinator(eventHandler))
Expand All @@ -71,7 +73,8 @@ class DefaultWebAuthenticationProviderTest {
assertThat((eventHandler[1] as CustomizeBrowserEvent).queryIntentServicesFlags).isEqualTo(0)
}

@Test fun testLaunchWithEnabledBrowsers() {
@Test
fun testLaunchWithEnabledBrowsers() {
installCustomTabsProvider("com.android.chrome.beta")
installCustomTabsProvider("com.android.chrome")
ShadowResolveInfo.newResolveInfo(
Expand All @@ -88,7 +91,8 @@ class DefaultWebAuthenticationProviderTest {
assertThat(cctActivity.`package`).isEqualTo("com.android.chrome")
}

@Test fun testLaunchWithPreferredBrowsers() {
@Test
fun testLaunchWithPreferredBrowsers() {
installCustomTabsProvider("com.android.chrome.beta")
installCustomTabsProvider("my.custom.preferred.browser")
installCustomTabsProvider("com.android.chrome")
Expand All @@ -110,14 +114,30 @@ class DefaultWebAuthenticationProviderTest {
assertThat(cctActivity.`package`).isEqualTo("my.custom.preferred.browser")
}

@Test fun testLaunchCausesActivityNotFound() {
@Test
fun testLaunchCausesActivityNotFound() {
shadowOf(RuntimeEnvironment.getApplication()).checkActivities(true)
val activity = Robolectric.buildActivity(Activity::class.java)
val webAuthenticationProvider = DefaultWebAuthenticationProvider(EventCoordinator(emptyList()))
val exception = webAuthenticationProvider.launch(activity.get(), "https://example.com/not_used".toHttpUrl())
assertThat(exception).isInstanceOf(ActivityNotFoundException::class.java)
}

@Test
fun testLaunchWithCustomTabsProviderThatIsAlsoBrowser() {
installCustomTabsProvider("my.custom.simpleCustomTabProvider", false)
installCustomTabsProvider("my.custom.browserCustomTabProvider")

val activity = Robolectric.buildActivity(Activity::class.java)
val webAuthenticationProvider = DefaultWebAuthenticationProvider(EventCoordinator(emptyList()))

assertThat(webAuthenticationProvider.launch(activity.get(), "https://example.com/not_used".toHttpUrl())).isNull()
val activityShadow = shadowOf(activity.get())
val cctActivity = activityShadow.nextStartedActivity
assertThat(cctActivity.action).isEqualTo("android.intent.action.VIEW")
assertThat(cctActivity.`package`).isEqualTo("my.custom.browserCustomTabProvider")
}

// Copyright 2019 Google Inc. All Rights Reserved.
// https://chromium.googlesource.com/custom-tabs-client/+/refs/heads/main/customtabs/junit/src/android/support/customtabs/trusted/TwaProviderPickerTest.java
private fun installBrowser(packageName: String) {
Expand All @@ -126,6 +146,7 @@ class DefaultWebAuthenticationProviderTest {
.setData(Uri.parse("http://"))
.setAction(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setPackage(packageName)
val resolveInfo = ResolveInfo()
resolveInfo.activityInfo = ActivityInfo()
resolveInfo.activityInfo.packageName = packageName
Expand All @@ -134,8 +155,10 @@ class DefaultWebAuthenticationProviderTest {
packageManager.addResolveInfoForIntent(intent, resolveInfo)
}

private fun installCustomTabsProvider(packageName: String) {
installBrowser(packageName)
private fun installCustomTabsProvider(packageName: String, installAsBrowser: Boolean = true) {
if (installAsBrowser) {
installBrowser(packageName)
}
val intent = Intent().setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION)
val resolveInfo = ResolveInfo()
resolveInfo.serviceInfo = ServiceInfo()
Expand Down