Fix: isolate auto-login errors from confirmation code errors
- confirmRegistration: wrap auto-login in separate try-catch so a failed login after confirmation never shows as 'wrong code' to the user. If auto-login fails, user is sent to LoginScreen to log in manually. - SessionManager.login: always reconnect fresh (disconnect + enableReconnect + connect) instead of reusing the stale post-registration TCP connection. Fixes login failures caused by server closing the registration connection. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -64,9 +64,9 @@ class SessionManager @Inject constructor(
|
||||
// Re-authenticate automatically whenever the connection is (re)established.
|
||||
// During the initial login() call, lastEmail is null (cleared before connect),
|
||||
// so this handler is a no-op for the first connection.
|
||||
connection.onConnected = {
|
||||
val email = lastEmail ?: return@onConnected
|
||||
val key = lastRsaPrivateKey ?: return@onConnected
|
||||
connection.onConnected = reconnect@{
|
||||
val email = lastEmail ?: return@reconnect
|
||||
val key = lastRsaPrivateKey ?: return@reconnect
|
||||
scope.launch {
|
||||
try {
|
||||
val session = performAuthHandshake(email, key, lastDeviceId, "Android")
|
||||
@@ -100,9 +100,13 @@ class SessionManager @Inject constructor(
|
||||
lastRsaPrivateKey = null
|
||||
|
||||
try {
|
||||
if (connection.state.value != ConnectionManager.State.CONNECTED) {
|
||||
connection.connect(host, port, useTls)
|
||||
// Always start with a fresh connection.
|
||||
// This handles stale post-registration connections and ensures reconnect is armed.
|
||||
if (connection.state.value == ConnectionManager.State.CONNECTED) {
|
||||
connection.disconnect()
|
||||
}
|
||||
connection.enableReconnect()
|
||||
connection.connect(host, port, useTls)
|
||||
|
||||
val session = performAuthHandshake(email, rsaPrivateKey, deviceId, deviceName)
|
||||
|
||||
|
||||
@@ -211,32 +211,47 @@ class AuthViewModel @Inject constructor(
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, loadingMessage = "Potvrzuji kód…", error = null) }
|
||||
try {
|
||||
// Step 1: Verify the email code — this is the only thing that should report
|
||||
// "wrong code". Any error here is genuinely a bad/expired code.
|
||||
sessionManager.confirmRegistration(email, code)
|
||||
|
||||
// Auto-login immediately after confirmation using the already-decrypted
|
||||
// key material from register(). This avoids re-asking for the password.
|
||||
// Step 2: Try auto-login using the key material saved during register().
|
||||
// This is best-effort — failure here does NOT mean the code was wrong.
|
||||
// On failure the user is sent back to LoginScreen to log in manually.
|
||||
val auth = pendingAuth
|
||||
var loggedIn = false
|
||||
if (auth != null && auth.email == email) {
|
||||
_uiState.update { it.copy(loadingMessage = "Přihlašuji se…") }
|
||||
val state = _uiState.value
|
||||
sessionManager.login(
|
||||
email = email,
|
||||
rsaPrivateKey = auth.rsaPrivate,
|
||||
host = state.serverHost,
|
||||
port = state.serverPort,
|
||||
useTls = state.useTls,
|
||||
)
|
||||
// Init local DB encryption key (no password needed — bytes already decrypted)
|
||||
keyStorage.initLocalKey(auth.identityPrivateBytes)
|
||||
pendingAuth = null // consumed — clear for security
|
||||
try {
|
||||
_uiState.update { it.copy(loadingMessage = "Přihlašuji se…") }
|
||||
val state = _uiState.value
|
||||
sessionManager.login(
|
||||
email = email,
|
||||
rsaPrivateKey = auth.rsaPrivate,
|
||||
host = state.serverHost,
|
||||
port = state.serverPort,
|
||||
useTls = state.useTls,
|
||||
)
|
||||
keyStorage.initLocalKey(auth.identityPrivateBytes)
|
||||
loggedIn = true
|
||||
} catch (loginEx: Exception) {
|
||||
// Auto-login failed (e.g. stale connection, transient server error).
|
||||
// Log it but don't surface to user as a "wrong code" error.
|
||||
android.util.Log.w(
|
||||
"AuthViewModel",
|
||||
"Auto-login after confirm failed, user must log in manually: ${loginEx.message}"
|
||||
)
|
||||
} finally {
|
||||
pendingAuth = null
|
||||
}
|
||||
}
|
||||
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
isLoading = false,
|
||||
loadingMessage = null,
|
||||
isLoggedIn = true,
|
||||
isLoggedIn = loggedIn,
|
||||
needsConfirmation = false,
|
||||
hasExistingAccount = true,
|
||||
)
|
||||
}
|
||||
} catch (e: AuthException) {
|
||||
|
||||
Reference in New Issue
Block a user