Skip to content

Commit 28f1931

Browse files
authored
android: touchless auth key login (#667)
updates tailscale/corp#29482 If an authKey is detected in the mdm payload, we will now skip the onboarding flows and several of the other non-mandatory permission prompts. Signed-off-by: Jonathan Nobels <[email protected]>
1 parent b999309 commit 28f1931

File tree

4 files changed

+36
-18
lines changed

4 files changed

+36
-18
lines changed

android/src/main/java/com/tailscale/ipn/App.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.app.PendingIntent
1010
import android.content.Context
1111
import android.content.Intent
1212
import android.content.IntentFilter
13+
import android.content.RestrictionsManager
1314
import android.content.SharedPreferences
1415
import android.content.pm.PackageManager
1516
import android.net.ConnectivityManager
@@ -48,7 +49,6 @@ import kotlinx.coroutines.launch
4849
import kotlinx.serialization.encodeToString
4950
import kotlinx.serialization.json.Json
5051
import libtailscale.Libtailscale
51-
import java.io.File
5252
import java.io.IOException
5353
import java.net.NetworkInterface
5454
import java.security.GeneralSecurityException
@@ -157,13 +157,16 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
157157
if (storedUri != null && storedUri.toString().startsWith("content://")) {
158158
startLibtailscale(storedUri.toString())
159159
} else {
160-
startLibtailscale(this.getFilesDir().absolutePath)
160+
startLibtailscale(this.filesDir.absolutePath)
161161
}
162162
healthNotifier = HealthNotifier(Notifier.health, Notifier.state, applicationScope)
163163
connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
164164
NetworkChangeCallback.monitorDnsChanges(connectivityManager, dns)
165165
initViewModels()
166166
applicationScope.launch {
167+
val rm = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
168+
MDMSettings.update(get(), rm)
169+
167170
Notifier.state.collect { _ ->
168171
combine(Notifier.state, MDMSettings.forceEnabled.flow, Notifier.prefs, Notifier.netmap) {
169172
state,

android/src/main/java/com/tailscale/ipn/MainActivity.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ class MainActivity : ComponentActivity() {
137137
val rm = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
138138
MDMSettings.update(App.get(), rm)
139139

140+
if (MDMSettings.onboardingFlow.flow.value.value == ShowHide.Hide ||
141+
MDMSettings.authKey.flow.value.value != null) {
142+
setIntroScreenViewed(true)
143+
}
144+
140145
// (jonathan) TODO: Force the app to be portrait on small screens until we have
141146
// proper landscape layout support
142147
if (!isLandscapeCapable()) {
@@ -367,7 +372,7 @@ class MainActivity : ComponentActivity() {
367372
onNavigateHome = backTo("main"), backTo("userSwitcher"))
368373
}
369374
}
370-
if (shouldDisplayOnboarding()) {
375+
if (isIntroScreenViewedSet()) {
371376
navController.navigate("intro")
372377
setIntroScreenViewed(true)
373378
}
@@ -505,10 +510,6 @@ class MainActivity : ComponentActivity() {
505510
lifecycleScope.launch(Dispatchers.IO) { MDMSettings.update(App.get(), restrictionsManager) }
506511
}
507512

508-
override fun onStart() {
509-
super.onStart()
510-
}
511-
512513
override fun onStop() {
513514
super.onStop()
514515
val restrictionsManager =
@@ -525,11 +526,8 @@ class MainActivity : ComponentActivity() {
525526
startActivity(intent)
526527
}
527528

528-
private fun shouldDisplayOnboarding(): Boolean {
529-
val onboardingFlowShowHide = MDMSettings.onboardingFlow.flow.value.value
530-
val introSeen =
531-
getSharedPreferences("introScreen", Context.MODE_PRIVATE).getBoolean("seen", false)
532-
return (onboardingFlowShowHide == ShowHide.Show && !introSeen)
529+
private fun isIntroScreenViewedSet(): Boolean {
530+
return !getSharedPreferences("introScreen", Context.MODE_PRIVATE).getBoolean("seen", false)
533531
}
534532

535533
private fun setIntroScreenViewed(seen: Boolean) {

android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,15 @@ fun MainView(
215215

216216
when (state) {
217217
Ipn.State.Running -> {
218-
219-
PromptPermissionsIfNecessary()
220218
viewModel.maybeRequestVpnPermission()
221219
LaunchVpnPermissionIfNeeded(viewModel)
222-
LaunchedEffect(state) {
223-
if (state == Ipn.State.Running && !isAndroidTV()) {
224-
viewModel.checkIfTaildropDirectorySelected()
220+
PromptForMissingPermissions(viewModel)
221+
222+
if (!viewModel.skipPromptsForAuthKeyLogin()) {
223+
LaunchedEffect(state) {
224+
if (state == Ipn.State.Running && !isAndroidTV()) {
225+
viewModel.checkIfTaildropDirectorySelected()
226+
}
225227
}
226228
}
227229

@@ -795,7 +797,11 @@ fun ExpiryNotification(netmap: Netmap.NetworkMap?, action: () -> Unit = {}) {
795797

796798
@OptIn(ExperimentalPermissionsApi::class)
797799
@Composable
798-
fun PromptPermissionsIfNecessary() {
800+
fun PromptForMissingPermissions(viewModel: MainViewModel) {
801+
if (viewModel.skipPromptsForAuthKeyLogin()) {
802+
return
803+
}
804+
799805
Permissions.prompt.forEach { (permission, state) ->
800806
ErrorDialog(
801807
title = permission.title,

android/src/main/java/com/tailscale/ipn/ui/viewModel/MainViewModel.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() {
126126
this.pingViewModel.handleDismissal()
127127
}
128128

129+
// Returns true if we should skip all of the user-interactive permissions prompts
130+
// (with the exception of the VPN permission prompt)
131+
fun skipPromptsForAuthKeyLogin(): Boolean {
132+
val v = MDMSettings.authKey.flow.value.value
133+
return v != null && v != ""
134+
}
135+
129136
private val peerCategorizer = PeerCategorizer()
130137

131138
init {
@@ -219,6 +226,10 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() {
219226
}
220227

221228
fun checkIfTaildropDirectorySelected() {
229+
if (skipPromptsForAuthKeyLogin()) {
230+
return
231+
}
232+
222233
val app = App.get()
223234
val storedUri = app.getStoredDirectoryUri()
224235
if (storedUri == null) {

0 commit comments

Comments
 (0)