fix(android): preserve bootstrap auth for manual reconnect

This commit is contained in:
Ayaan Zaidi 2026-03-31 14:58:53 +05:30
parent eb84d91a80
commit c1269eddb8
4 changed files with 53 additions and 1 deletions

View File

@ -91,6 +91,7 @@ class MainViewModel(app: Application) : AndroidViewModel(app) {
val manualPort: StateFlow<Int> = prefs.manualPort
val manualTls: StateFlow<Boolean> = prefs.manualTls
val gatewayToken: StateFlow<String> = prefs.gatewayToken
val gatewayBootstrapToken: StateFlow<String> = prefs.gatewayBootstrapToken
val onboardingCompleted: StateFlow<Boolean> = prefs.onboardingCompleted
val canvasDebugStatusEnabled: StateFlow<Boolean> = prefs.canvasDebugStatusEnabled
val speakerEnabled: StateFlow<Boolean> = prefs.speakerEnabled

View File

@ -72,6 +72,7 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
val manualTls by viewModel.manualTls.collectAsState()
val manualEnabled by viewModel.manualEnabled.collectAsState()
val gatewayToken by viewModel.gatewayToken.collectAsState()
val gatewayBootstrapToken by viewModel.gatewayBootstrapToken.collectAsState()
val pendingTrust by viewModel.pendingGatewayTrust.collectAsState()
var advancedOpen by rememberSaveable { mutableStateOf(false) }
@ -244,6 +245,7 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
manualHost = manualHostInput,
manualPort = manualPortInput,
manualTls = manualTlsInput,
fallbackBootstrapToken = gatewayBootstrapToken,
fallbackToken = gatewayToken,
fallbackPassword = passwordInput,
)

View File

@ -40,6 +40,7 @@ internal fun resolveGatewayConnectConfig(
manualHost: String,
manualPort: String,
manualTls: Boolean,
fallbackBootstrapToken: String,
fallbackToken: String,
fallbackPassword: String,
): GatewayConnectConfig? {
@ -75,7 +76,12 @@ internal fun resolveGatewayConnectConfig(
host = parsed.host,
port = parsed.port,
tls = parsed.tls,
bootstrapToken = "",
bootstrapToken =
if (fallbackToken.isBlank() && fallbackPassword.isBlank()) {
fallbackBootstrapToken.trim()
} else {
""
},
token = fallbackToken.trim(),
password = fallbackPassword.trim(),
)

View File

@ -158,6 +158,7 @@ class GatewayConfigResolverTest {
manualHost = "",
manualPort = "",
manualTls = true,
fallbackBootstrapToken = "",
fallbackToken = "shared-token",
fallbackPassword = "shared-password",
)
@ -182,6 +183,7 @@ class GatewayConfigResolverTest {
manualHost = "",
manualPort = "",
manualTls = true,
fallbackBootstrapToken = "",
fallbackToken = "shared-token",
fallbackPassword = "shared-password",
)
@ -194,6 +196,47 @@ class GatewayConfigResolverTest {
assertNull(resolved?.password?.takeIf { it.isNotEmpty() })
}
@Test
fun resolveGatewayConnectConfigManualPreservesBootstrapTokenWhenNoReplacementAuthExists() {
val resolved =
resolveGatewayConnectConfig(
useSetupCode = false,
setupCode = "",
manualHost = "192.168.31.100",
manualPort = "18789",
manualTls = false,
fallbackBootstrapToken = "bootstrap-1",
fallbackToken = "",
fallbackPassword = "",
)
assertEquals("192.168.31.100", resolved?.host)
assertEquals(18789, resolved?.port)
assertEquals(false, resolved?.tls)
assertEquals("bootstrap-1", resolved?.bootstrapToken)
assertEquals("", resolved?.token)
assertEquals("", resolved?.password)
}
@Test
fun resolveGatewayConnectConfigManualDropsBootstrapTokenWhenReplacementPasswordExists() {
val resolved =
resolveGatewayConnectConfig(
useSetupCode = false,
setupCode = "",
manualHost = "192.168.31.100",
manualPort = "18789",
manualTls = false,
fallbackBootstrapToken = "bootstrap-1",
fallbackToken = "",
fallbackPassword = "password-1",
)
assertEquals("", resolved?.bootstrapToken)
assertEquals("", resolved?.token)
assertEquals("password-1", resolved?.password)
}
private fun encodeSetupCode(payloadJson: String): String {
return Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.toByteArray(Charsets.UTF_8))
}