android: do not stop running on login, and edit prefs after startLoginInteractive

Previously: start, edit prefs with wantRunning=false, then startLoginInteractive
Now: 1. editPrefs() with WantRunning=true, LoggedOut=false if AuthKey != null
     2. start() -> boots tailscaled; if an AuthKey was provided and WantRunning/LoggedOut were set
        to true/false in step 1, backend will attempt to log in immediately.
     3. startLoginInteractive() if LoggedOut wasn't set to false in step 1 and the user will be
        directed to a URL to compelte authentication
Do not call wantRunning=false; the route clearing issue requiring that is resolved.

Updates tailscale/corp#24002

Signed-off-by: kari-ts <kari@tailscale.com>
jonathan/login
kari-ts 7 months ago committed by Jonathan Nobels
parent a5a5cbb2d5
commit 2cfebbe495

@ -95,6 +95,7 @@ class Ipn {
var ExitNodeIDSet: Boolean? = null,
var ExitNodeAllowLANAccessSet: Boolean? = null,
var WantRunningSet: Boolean? = null,
var LoggedOutSet: Boolean? = null,
var ShieldsUpSet: Boolean? = null,
var AdvertiseRoutesSet: Boolean? = null,
var ForceDaemonSet: Boolean? = null,
@ -144,6 +145,12 @@ class Ipn {
WantRunningSet = true
}
var LoggedOut: Boolean? = null
set (value) {
field = value
LoggedOutSet = true
}
var ShieldsUp: Boolean? = null
set(value) {
field = value

@ -144,52 +144,60 @@ open class IpnViewModel : ViewModel() {
// Login/Logout
/**
* Order of operations:
* 1. editPrefs() with WantRunning=true, LoggedOut=false if AuthKey != null
* 2. start() -> boots tailscaled; if an AuthKey was provided and WantRunning/LoggedOut were set
* to true/false in step 1, backend will attempt to log in immediately.
* 3. startLoginInteractive() if LoggedOut wasn't set to false in step 1 and the user will be
* directed to a URL to complete authentication
*
* Any failure shortcircuits the chain and invokes completionHandler once.
*/
fun login(
maskedPrefs: Ipn.MaskedPrefs? = null,
authKey: String? = null,
completionHandler: (Result<Unit>) -> Unit = {}
) {
val client = Client(viewModelScope)
val loginAction = {
Client(viewModelScope).startLoginInteractive { result ->
result
.onSuccess { TSLog.d(TAG, "Login started: $it") }
.onFailure { TSLog.e(TAG, "Error starting login: ${it.message}") }
completionHandler(result)
}
}
// Need to stop running before logging in to clear routes:
// https://linear.app/tailscale/issue/ENG-3441/routesdns-is-not-cleared-when-switching-profiles-or-reauthenticating
val stopThenLogin = {
Client(viewModelScope).editPrefs(Ipn.MaskedPrefs().apply { WantRunning = false }) { result ->
result
.onSuccess { loginAction() }
.onFailure { TSLog.e(TAG, "Error setting wantRunning to false: ${it.message}") }
}
}
val startAction = {
Client(viewModelScope).start(Ipn.Options(AuthKey = authKey)) { start ->
start.onFailure { completionHandler(Result.failure(it)) }.onSuccess { stopThenLogin() }
}
val finalMaskedPrefs = maskedPrefs?.copy() ?: Ipn.MaskedPrefs()
finalMaskedPrefs.WantRunning = true
if (authKey != null) {
finalMaskedPrefs.LoggedOut = false
}
// If an MDM control URL is set, we will always use that in lieu of anything the user sets.
var prefs = maskedPrefs
val mdmControlURL = MDMSettings.loginURL.flow.value.value
if (mdmControlURL != null) {
prefs = prefs ?: Ipn.MaskedPrefs()
prefs.ControlURL = mdmControlURL
TSLog.d(TAG, "Overriding control URL with MDM value: $mdmControlURL")
client.editPrefs(finalMaskedPrefs) { editResult ->
editResult
.onFailure {
TSLog.e(TAG, "editPrefs() failed: ${it.message}")
completionHandler(Result.failure(it))
}
.onSuccess {
val opts = Ipn.Options(UpdatePrefs = editResult.getOrThrow(), AuthKey = authKey)
client.start(opts) { startResult ->
startResult
.onFailure {
TSLog.e(TAG, "start() failed: ${it.message}")
completionHandler(Result.failure(it))
}
.onSuccess {
if (authKey.isNullOrEmpty()) {
client.startLoginInteractive { loginResult ->
loginResult
.onFailure {
TSLog.e(TAG, "startLoginInteractive() failed: ${it.message}")
completionHandler(Result.failure(it))
}
.onSuccess { completionHandler(Result.success(Unit)) }
}
} else {
completionHandler(Result.success(Unit))
}
}
}
}
}
prefs?.let {
Client(viewModelScope).editPrefs(it) { result ->
result.onFailure { completionHandler(Result.failure(it)) }.onSuccess { startAction() }
}
} ?: run { startAction() }
}
fun loginWithAuthKey(authKey: String, completionHandler: (Result<Unit>) -> Unit = {}) {

Loading…
Cancel
Save