From 5ce531fd44905431bf3e2d541eccf9672e767bc7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Aug 2026 17:30:37 +0200 Subject: [PATCH] Ensure we create a fresh Client when the sessionId is know after authentication. --- .../matrix/impl/RustMatrixClientFactory.kt | 11 +-- .../auth/RustMatrixAuthenticationService.kt | 91 ++++++++----------- 2 files changed, 38 insertions(+), 64 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index de29e7a953..db391b3fdc 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -35,7 +35,6 @@ import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.withContext -import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.CrossProcessLockConfig import org.matrix.rustcomponents.sdk.RequestConfig @@ -118,14 +117,6 @@ class RustMatrixClientFactory( client.restoreSession(sessionData.toSession()) - create(client, sessionData, isMessageSearchAvailable) - } - - suspend fun create( - client: Client, - sessionData: SessionData, - isMessageSearchAvailable: Boolean, - ): RustMatrixClient { val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens() client.setUtdDelegate(UtdTracker(analyticsService)) @@ -136,7 +127,7 @@ class RustMatrixClientFactory( .withProfilesExtension() .finish() - return RustMatrixClient( + RustMatrixClient( sessionPaths = sessionData.getSessionPaths(), innerClient = client, sessionStore = sessionStore, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index a175f253ca..e0758b590d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -44,6 +44,7 @@ import io.element.android.libraries.matrix.impl.keys.SecretGenerator import io.element.android.libraries.matrix.impl.mapper.toSessionData import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory import io.element.android.libraries.sessionstorage.api.LoginType +import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.first @@ -101,17 +102,7 @@ class RustMatrixAuthenticationService( runCatchingExceptions { val sessionData = sessionStore.getSession(sessionId.value) if (sessionData != null) { - if (sessionData.isTokenValid) { - // Use the sessionData.passphrase, which can be null for a previously created session - if (sessionData.passphrase == null) { - Timber.w("Restoring a session without a passphrase") - } else { - Timber.w("Restoring a session with a passphrase") - } - rustMatrixClientFactory.create(sessionData) - } else { - throw SessionRestorationException.InvalidToken() - } + restoreSession(sessionData) } else { throw SessionRestorationException.MissingSession(sessionId) } @@ -120,6 +111,19 @@ class RustMatrixAuthenticationService( } } + private suspend fun restoreSession(sessionData: SessionData): MatrixClient { + if (!sessionData.isTokenValid) { + throw SessionRestorationException.InvalidToken() + } + // Use the sessionData.passphrase, which can be null for a previously created session + if (sessionData.passphrase == null) { + Timber.w("Restoring a session without a passphrase") + } else { + Timber.w("Restoring a session with a passphrase") + } + return rustMatrixClientFactory.create(sessionData) + } + private fun getDatabaseKey(): ClientSecret { Timber.d("New sessions will be encrypted with a raw key") return secretGenerator.generateKey() @@ -137,7 +141,7 @@ class RustMatrixAuthenticationService( client.homeserverLoginDetails().map() }.onFailure { - clear(destroyClient = true) + clear() }.mapFailure { failure -> Timber.e(failure, "Failed to set homeserver to $homeserver") failure.mapAuthenticationException() @@ -165,18 +169,7 @@ class RustMatrixAuthenticationService( passphrase = pendingKey.formattedAsString(), sessionPaths = currentSessionPaths, ) - val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) - - // Apply enterprise hooks to the newly created client as soon as possible - clientEnterpriseHook(matrixClient) - - newMatrixClientObservers.forEach { it.invoke(matrixClient) } - sessionStore.addSession(sessionData) - - // Clean up the strong reference held here since it's no longer necessary - clear(destroyClient = false) - - SessionId(sessionData.userId) + finalizeClientCreation(sessionData) }.mapFailure { failure -> Timber.e(failure, "Failed to login") failure.mapAuthenticationException() @@ -305,20 +298,7 @@ class RustMatrixAuthenticationService( passphrase = pendingKey.formattedAsString(), sessionPaths = currentSessionPaths, ) - val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) - - // Apply enterprise hooks to the newly created client as soon as possible - clientEnterpriseHook(matrixClient) - - matrixClient.waitForKnownVerificationState() - - newMatrixClientObservers.forEach { it.invoke(matrixClient) } - sessionStore.addSession(sessionData) - - // Clean up the strong reference held here since it's no longer necessary - clear(destroyClient = false) - - SessionId(sessionData.userId) + finalizeClientCreation(sessionData) }.mapFailure { failure -> Timber.e(failure, "Failed to login with OAuth") failure.mapAuthenticationException() @@ -374,18 +354,7 @@ class RustMatrixAuthenticationService( passphrase = pendingKey.formattedAsString(), sessionPaths = emptySessionPaths, ) - val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) - - // Apply enterprise hooks to the newly created client as soon as possible - clientEnterpriseHook(matrixClient) - - newMatrixClientObservers.forEach { it.invoke(matrixClient) } - sessionStore.addSession(sessionData) - - // Clean up the strong reference held here since it's no longer necessary - clear(destroyClient = false) - - SessionId(sessionData.userId) + finalizeClientCreation(sessionData) }.mapFailure { when (it) { is QrCodeDecodeException -> QrErrorMapper.map(it) @@ -400,6 +369,22 @@ class RustMatrixAuthenticationService( } } + private suspend fun finalizeClientCreation(sessionData: SessionData): SessionId { + val matrixClient = restoreSession(sessionData) + // Apply enterprise hooks to the newly created client as soon as possible + clientEnterpriseHook(matrixClient) + + matrixClient.waitForKnownVerificationState() + + newMatrixClientObservers.forEach { it.invoke(matrixClient) } + sessionStore.addSession(sessionData) + + // Clean up the strong reference held here since it's no longer necessary + clear() + + return SessionId(sessionData.userId) + } + private suspend fun makeClient( sessionPaths: SessionPaths, config: suspend ClientBuilder.() -> ClientBuilder, @@ -449,10 +434,8 @@ class RustMatrixAuthenticationService( .build() } - private fun clear(destroyClient: Boolean) { - if (destroyClient) { - currentClient?.close() - } + private fun clear() { + currentClient?.close() currentClient = null }