Skip to content

Commit 4deb9bf

Browse files
committed
Multi accounts - Prevent user from logging twice with the same account
1 parent 2963cba commit 4deb9bf

File tree

4 files changed

+37
-6
lines changed

4 files changed

+37
-6
lines changed

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/login/LoginModeView.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
2323
import io.element.android.libraries.designsystem.preview.ElementPreview
2424
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
2525
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
26+
import io.element.android.libraries.matrix.api.auth.AuthenticationException
2627
import io.element.android.libraries.matrix.api.auth.OidcDetails
2728
import io.element.android.libraries.ui.strings.CommonStrings
2829

@@ -89,6 +90,13 @@ fun LoginModeView(
8990
onSubmit = onClearError,
9091
)
9192
}
93+
is AuthenticationException.AccountAlreadyLoggedIn -> {
94+
// TODO i18n
95+
ErrorDialog(
96+
content = "You're already logged in on this device as ${error.message}.",
97+
onSubmit = onClearError,
98+
)
99+
}
92100
else -> {
93101
ErrorDialog(
94102
content = stringResource(CommonStrings.error_unknown),

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.libraries.matrix.api.auth
99

1010
sealed class AuthenticationException(message: String) : Exception(message) {
11+
class AccountAlreadyLoggedIn(userId: String) : AuthenticationException(userId)
1112
class InvalidServerName(message: String) : AuthenticationException(message)
1213
class SlidingSyncVersion(message: String) : AuthenticationException(message)
1314
class Oidc(message: String) : AuthenticationException(message)

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.matrix.rustcomponents.sdk.OidcException
1414
fun Throwable.mapAuthenticationException(): AuthenticationException {
1515
val message = this.message ?: "Unknown error"
1616
return when (this) {
17+
is AuthenticationException -> this
1718
is ClientBuildException -> when (this) {
1819
is ClientBuildException.Generic -> AuthenticationException.Generic(message)
1920
is ClientBuildException.InvalidServerName -> AuthenticationException.InvalidServerName(message)

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
1515
import io.element.android.libraries.core.extensions.mapFailure
1616
import io.element.android.libraries.core.extensions.runCatchingExceptions
1717
import io.element.android.libraries.matrix.api.MatrixClient
18+
import io.element.android.libraries.matrix.api.auth.AuthenticationException
1819
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
1920
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
2021
import io.element.android.libraries.matrix.api.auth.OidcDetails
@@ -149,6 +150,8 @@ class RustMatrixAuthenticationService(
149150
val client = currentClient ?: error("You need to call `setHomeserver()` first")
150151
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
151152
client.login(username, password, "Element X Android", null)
153+
// Ensure that the user is not already logged in with the same account
154+
ensureNotAlreadyLoggedIn(client)
152155
val sessionData = client.session()
153156
.toSessionData(
154157
isTokenValid = true,
@@ -237,17 +240,19 @@ class RustMatrixAuthenticationService(
237240
val client = currentClient ?: error("You need to call `setHomeserver()` first")
238241
val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first")
239242
client.loginWithOidcCallback(callbackUrl)
243+
244+
// Free the pending data since we won't use it to abort the flow anymore
245+
pendingOAuthAuthorizationData?.close()
246+
pendingOAuthAuthorizationData = null
247+
248+
// Ensure that the user is not already logged in with the same account
249+
ensureNotAlreadyLoggedIn(client)
240250
val sessionData = client.session().toSessionData(
241251
isTokenValid = true,
242252
loginType = LoginType.OIDC,
243253
passphrase = pendingPassphrase,
244254
sessionPaths = currentSessionPaths,
245255
)
246-
247-
// Free the pending data since we won't use it to abort the flow anymore
248-
pendingOAuthAuthorizationData?.close()
249-
pendingOAuthAuthorizationData = null
250-
251256
val matrixClient = rustMatrixClientFactory.create(client)
252257
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
253258
sessionStore.storeData(sessionData)
@@ -263,6 +268,21 @@ class RustMatrixAuthenticationService(
263268
}
264269
}
265270

271+
@Throws(AuthenticationException.AccountAlreadyLoggedIn::class)
272+
private suspend fun ensureNotAlreadyLoggedIn(client: Client) {
273+
val newUserId = client.userId()
274+
val accountAlreadyLoggedIn = sessionStore.getAllSessions().any {
275+
it.userId == newUserId
276+
}
277+
if (accountAlreadyLoggedIn) {
278+
// Sign out the client, ignoring any error
279+
runCatchingExceptions {
280+
client.logout()
281+
}
282+
throw AuthenticationException.AccountAlreadyLoggedIn(newUserId)
283+
}
284+
}
285+
266286
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) =
267287
withContext(coroutineDispatchers.io) {
268288
val sdkQrCodeLoginData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData
@@ -285,7 +305,8 @@ class RustMatrixAuthenticationService(
285305
oidcConfiguration = oidcConfiguration,
286306
progressListener = progressListener,
287307
)
288-
308+
// Ensure that the user is not already logged in with the same account
309+
ensureNotAlreadyLoggedIn(client)
289310
val sessionData = client.session()
290311
.toSessionData(
291312
isTokenValid = true,

0 commit comments

Comments
 (0)