Ensure we create a fresh Client when the sessionId is know after authentication.

This commit is contained in:
Benoit Marty
2026-08-28 17:30:37 +02:00
parent 039c0df9ed
commit 5ce531fd44
2 changed files with 38 additions and 64 deletions
@@ -35,7 +35,6 @@ import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.ClientBuilder import org.matrix.rustcomponents.sdk.ClientBuilder
import org.matrix.rustcomponents.sdk.CrossProcessLockConfig import org.matrix.rustcomponents.sdk.CrossProcessLockConfig
import org.matrix.rustcomponents.sdk.RequestConfig import org.matrix.rustcomponents.sdk.RequestConfig
@@ -118,14 +117,6 @@ class RustMatrixClientFactory(
client.restoreSession(sessionData.toSession()) client.restoreSession(sessionData.toSession())
create(client, sessionData, isMessageSearchAvailable)
}
suspend fun create(
client: Client,
sessionData: SessionData,
isMessageSearchAvailable: Boolean,
): RustMatrixClient {
val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens() val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens()
client.setUtdDelegate(UtdTracker(analyticsService)) client.setUtdDelegate(UtdTracker(analyticsService))
@@ -136,7 +127,7 @@ class RustMatrixClientFactory(
.withProfilesExtension() .withProfilesExtension()
.finish() .finish()
return RustMatrixClient( RustMatrixClient(
sessionPaths = sessionData.getSessionPaths(), sessionPaths = sessionData.getSessionPaths(),
innerClient = client, innerClient = client,
sessionStore = sessionStore, sessionStore = sessionStore,
@@ -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.mapper.toSessionData
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
import io.element.android.libraries.sessionstorage.api.LoginType 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 io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@@ -101,17 +102,7 @@ class RustMatrixAuthenticationService(
runCatchingExceptions { runCatchingExceptions {
val sessionData = sessionStore.getSession(sessionId.value) val sessionData = sessionStore.getSession(sessionId.value)
if (sessionData != null) { if (sessionData != null) {
if (sessionData.isTokenValid) { restoreSession(sessionData)
// 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()
}
} else { } else {
throw SessionRestorationException.MissingSession(sessionId) 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 { private fun getDatabaseKey(): ClientSecret {
Timber.d("New sessions will be encrypted with a raw key") Timber.d("New sessions will be encrypted with a raw key")
return secretGenerator.generateKey() return secretGenerator.generateKey()
@@ -137,7 +141,7 @@ class RustMatrixAuthenticationService(
client.homeserverLoginDetails().map() client.homeserverLoginDetails().map()
}.onFailure { }.onFailure {
clear(destroyClient = true) clear()
}.mapFailure { failure -> }.mapFailure { failure ->
Timber.e(failure, "Failed to set homeserver to $homeserver") Timber.e(failure, "Failed to set homeserver to $homeserver")
failure.mapAuthenticationException() failure.mapAuthenticationException()
@@ -165,18 +169,7 @@ class RustMatrixAuthenticationService(
passphrase = pendingKey.formattedAsString(), passphrase = pendingKey.formattedAsString(),
sessionPaths = currentSessionPaths, sessionPaths = currentSessionPaths,
) )
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) finalizeClientCreation(sessionData)
// 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)
}.mapFailure { failure -> }.mapFailure { failure ->
Timber.e(failure, "Failed to login") Timber.e(failure, "Failed to login")
failure.mapAuthenticationException() failure.mapAuthenticationException()
@@ -305,20 +298,7 @@ class RustMatrixAuthenticationService(
passphrase = pendingKey.formattedAsString(), passphrase = pendingKey.formattedAsString(),
sessionPaths = currentSessionPaths, sessionPaths = currentSessionPaths,
) )
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) finalizeClientCreation(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(destroyClient = false)
SessionId(sessionData.userId)
}.mapFailure { failure -> }.mapFailure { failure ->
Timber.e(failure, "Failed to login with OAuth") Timber.e(failure, "Failed to login with OAuth")
failure.mapAuthenticationException() failure.mapAuthenticationException()
@@ -374,18 +354,7 @@ class RustMatrixAuthenticationService(
passphrase = pendingKey.formattedAsString(), passphrase = pendingKey.formattedAsString(),
sessionPaths = emptySessionPaths, sessionPaths = emptySessionPaths,
) )
val matrixClient = rustMatrixClientFactory.create(client, sessionData, isMessageSearchAvailable()) finalizeClientCreation(sessionData)
// 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)
}.mapFailure { }.mapFailure {
when (it) { when (it) {
is QrCodeDecodeException -> QrErrorMapper.map(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( private suspend fun makeClient(
sessionPaths: SessionPaths, sessionPaths: SessionPaths,
config: suspend ClientBuilder.() -> ClientBuilder, config: suspend ClientBuilder.() -> ClientBuilder,
@@ -449,10 +434,8 @@ class RustMatrixAuthenticationService(
.build() .build()
} }
private fun clear(destroyClient: Boolean) { private fun clear() {
if (destroyClient) { currentClient?.close()
currentClient?.close()
}
currentClient = null currentClient = null
} }