fix(android): launch auth in CustomTab (#10371)

Unfortunately, Firefox on Android seems to have a bug where it only
allows one tab to intercept the custom URI scheme handler for our auth
redirect. This causes an issue where the first sign in works, but
subsequent ones do not because that first tab is still open.

Luckily the fix here is quite simple. By using Android's CustomTabs to
launch the activity, only one, sandboxed instance is ever open and the
URI intercept works reliably. Both Firefox and Chrome (and likely other
browsers) support CustomTabs, which means the user's default browser is
used, allowing cookies, password managers, etc to be used.

Related to this, this PR also fixes a bug where dismissing the launched
auth flow would result in it immediately relaunching, making it
impossible to get back to the app unless you force quit or complete the
sign in process.

Fixes #10318
This commit is contained in:
Jamil
2025-09-17 23:37:03 -04:00
committed by GitHub
parent 90d10a8634
commit f2ff5dfeca
2 changed files with 30 additions and 6 deletions

View File

@@ -1,12 +1,14 @@
/* Licensed under Apache 2.0 (C) 2024 Firezone, Inc. */
package dev.firezone.android.features.auth.ui
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import dagger.hilt.android.AndroidEntryPoint
import dev.firezone.android.R
import dev.firezone.android.core.presentation.MainActivity
@@ -16,6 +18,7 @@ import dev.firezone.android.databinding.ActivityAuthBinding
class AuthActivity : AppCompatActivity(R.layout.activity_auth) {
private lateinit var binding: ActivityAuthBinding
private val viewModel: AuthViewModel by viewModels()
private var hasLaunchedCustomTab = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -27,7 +30,12 @@ class AuthActivity : AppCompatActivity(R.layout.activity_auth) {
override fun onResume() {
super.onResume()
viewModel.onActivityResume()
if (hasLaunchedCustomTab) {
// User returned from Custom Tab without completing auth, navigate back to main app
navigateToSignIn()
} else {
viewModel.onActivityResume()
}
}
private fun setupActionObservers() {
@@ -43,12 +51,24 @@ class AuthActivity : AppCompatActivity(R.layout.activity_auth) {
}
}
// We can't close this webview because it's opened with ACTION_VIEW.
// If we want more control over it we need to embed our own WebView which
// has its own set of tradeoffs.
private fun setupWebView(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
hasLaunchedCustomTab = true
val customTabsIntent =
CustomTabsIntent
.Builder()
.setShowTitle(true)
.build()
val url = Uri.parse(url)
// Try to use Custom Tabs with the default browser first
try {
customTabsIntent.launchUrl(this, url)
} catch (e: ActivityNotFoundException) {
// Fallback to default browser if Custom Tabs unavailable
val intent = Intent(Intent.ACTION_VIEW, url)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}
private fun navigateToSignIn() {

View File

@@ -21,6 +21,10 @@ export default function Android() {
<Entries downloadLinks={downloadLinks} title="Android">
{/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */}
<Unreleased>
<ChangeItem pull="10371">
Fixes a bug that could prevent sign-ins from completing successfully
if Firefox is set as the default browser.
</ChangeItem>
<ChangeItem pull="10104">
Fixes an issue where DNS resources would resolve to a different IP
after signing out and back into Firezone. This would break