Skip to content

Oidc: Fallback to external browser instead of using Webview #4808

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 4, 2025
Merged
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 @@ -36,6 +36,7 @@ import io.element.android.features.login.impl.screens.createaccount.CreateAccoun
import io.element.android.features.login.impl.screens.loginpassword.LoginPasswordNode
import io.element.android.features.login.impl.screens.onboarding.OnBoardingNode
import io.element.android.features.login.impl.screens.searchaccountprovider.SearchAccountProviderNode
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs
Expand All @@ -45,7 +46,6 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.oidc.api.OidcAction
import io.element.android.libraries.oidc.api.OidcActionFlow
import io.element.android.libraries.oidc.api.OidcEntryPoint
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
Expand All @@ -57,7 +57,6 @@ class LoginFlowNode @AssistedInject constructor(
private val accountProviderDataSource: AccountProviderDataSource,
private val defaultLoginUserStory: DefaultLoginUserStory,
private val oidcActionFlow: OidcActionFlow,
private val oidcEntryPoint: OidcEntryPoint,
) : BaseFlowNode<LoginFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.OnBoarding,
Expand All @@ -74,15 +73,15 @@ class LoginFlowNode @AssistedInject constructor(
private var activity: Activity? = null
private var darkTheme: Boolean = false

private var customChromeTabStarted = false
private var externalAppStarted = false

override fun onBuilt() {
super.onBuilt()
defaultLoginUserStory.setLoginFlowIsDone(false)
lifecycle.subscribe(
onResume = {
if (customChromeTabStarted) {
customChromeTabStarted = false
if (externalAppStarted) {
externalAppStarted = false
// Workaround to detect that the Custom Chrome Tab has been closed
// If there is no coming OidcAction (that would end this Node),
// consider that the user has cancelled the login
Expand Down Expand Up @@ -122,9 +121,6 @@ class LoginFlowNode @AssistedInject constructor(

@Parcelize
data class CreateAccount(val url: String) : NavTarget

@Parcelize
data class OidcView(val oidcDetails: OidcDetails) : NavTarget
}

override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
Expand Down Expand Up @@ -249,9 +245,6 @@ class LoginFlowNode @AssistedInject constructor(
NavTarget.LoginPassword -> {
createNode<LoginPasswordNode>(buildContext)
}
is NavTarget.OidcView -> {
oidcEntryPoint.createFallbackWebViewNode(this, buildContext, navTarget.oidcDetails.url)
}
is NavTarget.CreateAccount -> {
val inputs = CreateAccountNode.Inputs(
url = navTarget.url,
Expand All @@ -262,15 +255,9 @@ class LoginFlowNode @AssistedInject constructor(
}

private fun navigateToMas(oidcDetails: OidcDetails) {
if (oidcEntryPoint.canUseCustomTab()) {
// In this case open a Chrome Custom tab
activity?.let {
customChromeTabStarted = true
oidcEntryPoint.openUrlInCustomTab(it, darkTheme, oidcDetails.url)
}
} else {
// Fallback to WebView mode
backstack.push(NavTarget.OidcView(oidcDetails))
activity?.let {
externalAppStarted = true
it.openUrlInChromeCustomTab(null, darkTheme, oidcDetails.url)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.securebackup.impl.reset.password.ResetIdentityPasswordNode
import io.element.android.features.securebackup.impl.reset.root.ResetIdentityRootNode
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
Expand All @@ -37,7 +38,6 @@ import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle
import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle
import io.element.android.libraries.oidc.api.OidcEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
Expand All @@ -51,7 +51,6 @@ class ResetIdentityFlowNode @AssistedInject constructor(
@Assisted plugins: List<Plugin>,
private val resetIdentityFlowManager: ResetIdentityFlowManager,
private val coroutineScope: CoroutineScope,
private val oidcEntryPoint: OidcEntryPoint,
) : BaseFlowNode<ResetIdentityFlowNode.NavTarget>(
backstack = BackStack(initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap),
buildContext = buildContext,
Expand All @@ -67,20 +66,18 @@ class ResetIdentityFlowNode @AssistedInject constructor(

@Parcelize
data object ResetPassword : NavTarget

@Parcelize
data class ResetOidc(val url: String) : NavTarget
}

private lateinit var activity: Activity
private var darkTheme: Boolean = false
private var resetJob: Job? = null

override fun onBuilt() {
super.onBuilt()

lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
// If the custom tab was opened, we need to cancel the reset job
// If the custom tab / Web browser was opened, we need to cancel the reset job
// when we come back to the node if the reset wasn't successful
coroutineScope.launch {
cancelResetJob()
Expand Down Expand Up @@ -115,9 +112,6 @@ class ResetIdentityFlowNode @AssistedInject constructor(
listOf(ResetIdentityPasswordNode.Inputs(handle))
)
}
is NavTarget.ResetOidc -> {
oidcEntryPoint.createFallbackWebViewNode(this, buildContext, navTarget.url)
}
}
}

Expand All @@ -135,11 +129,7 @@ class ResetIdentityFlowNode @AssistedInject constructor(
Timber.d("No reset handle return, the reset is done.")
}
is IdentityOidcResetHandle -> {
if (oidcEntryPoint.canUseCustomTab()) {
activity.openUrlInChromeCustomTab(null, false, handle.url)
} else {
backstack.push(NavTarget.ResetOidc(handle.url))
}
activity.openUrlInChromeCustomTab(null, darkTheme, handle.url)
resetJob = launch { handle.resetOidc() }
}
is IdentityPasswordResetHandle -> backstack.push(NavTarget.ResetPassword)
Expand All @@ -162,7 +152,7 @@ class ResetIdentityFlowNode @AssistedInject constructor(
if (!this::activity.isInitialized) {
activity = requireNotNull(LocalActivity.current)
}

darkTheme = !ElementTheme.isLightTheme
val startResetState by resetIdentityFlowManager.currentHandleFlow.collectAsState()
if (startResetState.isLoading()) {
ProgressDialog(
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/

package io.element.android.libraries.oidc.impl.customtab
package io.element.android.libraries.oidc.impl

import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading