diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt index a104f2c031..2d65cac43d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/auth/data/SessionParams.kt @@ -22,5 +22,6 @@ package im.vector.matrix.android.api.auth.data */ data class SessionParams( val credentials: Credentials, - val homeServerConnectionConfig: HomeServerConnectionConfig + val homeServerConnectionConfig: HomeServerConnectionConfig, + val isTokenValid: Boolean ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 13b9fe07d6..1c1a3600c8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -62,6 +62,11 @@ interface Session : */ val sessionParams: SessionParams + /** + * The session is valid, i.e. it has a valid token so far + */ + val isOpenable: Boolean + /** * Useful shortcut to get access to the userId */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionCreator.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionCreator.kt index f04f262d6e..95a9fbb506 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionCreator.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionCreator.kt @@ -60,7 +60,8 @@ internal class DefaultSessionCreator @Inject constructor( ?.also { Timber.d("Overriding identity server url to $it") } ?.let { Uri.parse(it) } ?: homeServerConnectionConfig.identityServerUri - )) + ), + isTokenValid = true) sessionParamsStore.save(sessionParams) return sessionManager.getOrCreateSession(sessionParams) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt index e57883f731..57c22b0053 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/SessionParamsStore.kt @@ -29,6 +29,8 @@ internal interface SessionParamsStore { suspend fun save(sessionParams: SessionParams) + suspend fun setTokenInvalid(userId: String) + suspend fun updateCredentials(newCredentials: Credentials) suspend fun delete(userId: String) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmMigration.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmMigration.kt index 5f1efb487b..6b86c3db98 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmMigration.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/AuthRealmMigration.kt @@ -24,7 +24,7 @@ internal class AuthRealmMigration : RealmMigration { companion object { // Current schema version - const val SCHEMA_VERSION = 1L + const val SCHEMA_VERSION = 2L } override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { @@ -46,5 +46,14 @@ internal class AuthRealmMigration : RealmMigration { .addField(PendingSessionEntityFields.IS_REGISTRATION_STARTED, Boolean::class.java) .addField(PendingSessionEntityFields.CURRENT_THREE_PID_DATA_JSON, String::class.java) } + + if (oldVersion <= 1) { + Timber.d("Step 1 -> 2") + Timber.d("Add boolean isTokenValid in SessionParamsEntity, with value true") + + realm.schema.get("SessionParamsEntity") + ?.addField(SessionParamsEntityFields.IS_TOKEN_VALID, Boolean::class.java) + ?.transform { it.set(SessionParamsEntityFields.IS_TOKEN_VALID, true) } + } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt index 6d0430da05..2a6ed732b6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/RealmSessionParamsStore.kt @@ -76,6 +76,26 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S } } + override suspend fun setTokenInvalid(userId: String) { + awaitTransaction(realmConfiguration) { realm -> + val currentSessionParams = realm + .where(SessionParamsEntity::class.java) + .equalTo(SessionParamsEntityFields.USER_ID, userId) + .findAll() + .firstOrNull() + + if (currentSessionParams == null) { + // Should not happen + "Session param not found for user $userId" + .let { Timber.w(it) } + .also { error(it) } + } else { + currentSessionParams.isTokenValid = false + } + } + } + + override suspend fun updateCredentials(newCredentials: Credentials) { awaitTransaction(realmConfiguration) { realm -> val currentSessionParams = realm @@ -92,7 +112,8 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S .also { error(it) } } else { val newSessionParams = currentSessionParams.copy( - credentials = newCredentials + credentials = newCredentials, + isTokenValid = true ) val entity = mapper.map(newSessionParams) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt index d0cc41d1ac..94aab18638 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsEntity.kt @@ -22,5 +22,8 @@ import io.realm.annotations.PrimaryKey internal open class SessionParamsEntity( @PrimaryKey var userId: String = "", var credentialsJson: String = "", - var homeServerConnectionConfigJson: String = "" + var homeServerConnectionConfigJson: String = "", + // Set to false when the token is invalid and the user has been soft logged out + // In case of hard logout, this object in deleted from DB + var isTokenValid: Boolean = true ) : RealmObject() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt index 8e64e86582..72e8087f3f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/auth/db/SessionParamsMapper.kt @@ -36,7 +36,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { if (credentials == null || homeServerConnectionConfig == null) { return null } - return SessionParams(credentials, homeServerConnectionConfig) + return SessionParams(credentials, homeServerConnectionConfig, entity.isTokenValid) } fun map(sessionParams: SessionParams?): SessionParamsEntity? { @@ -48,6 +48,10 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { if (credentialsJson == null || homeServerConnectionConfigJson == null) { return null } - return SessionParamsEntity(sessionParams.credentials.userId, credentialsJson, homeServerConnectionConfigJson) + return SessionParamsEntity( + sessionParams.credentials.userId, + credentialsJson, + homeServerConnectionConfigJson, + sessionParams.isTokenValid) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 144b68eeaf..d6a0206eca 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -42,10 +42,14 @@ import im.vector.matrix.android.api.session.signout.SignOutService import im.vector.matrix.android.api.session.sync.FilterService import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.user.UserService +import im.vector.matrix.android.internal.auth.SessionParamsStore import im.vector.matrix.android.internal.crypto.DefaultCryptoService import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.session.sync.job.SyncThread import im.vector.matrix.android.internal.session.sync.job.SyncWorker +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -72,6 +76,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private val secureStorageService: Lazy, private val syncThreadProvider: Provider, private val contentUrlResolver: ContentUrlResolver, + private val sessionParamsStore: SessionParamsStore, private val contentUploadProgressTracker: ContentUploadStateTracker, private val initialSyncProgressService: Lazy, private val homeServerCapabilitiesService: Lazy) @@ -94,6 +99,9 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se private var syncThread: SyncThread? = null + override val isOpenable: Boolean + get() = sessionParamsStore.get(myUserId)?.isTokenValid ?: false + @MainThread override fun open() { assertMainThread() @@ -171,6 +179,14 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se @Subscribe(threadMode = ThreadMode.MAIN) fun onGlobalError(globalError: GlobalError) { + if (globalError is GlobalError.InvalidToken + && globalError.softLogout) { + // Mark the token has invalid + GlobalScope.launch(Dispatchers.IO) { + sessionParamsStore.setTokenInvalid(myUserId) + } + } + sessionListeners.dispatchGlobalError(globalError) } diff --git a/vector/src/main/java/im/vector/riotx/features/MainActivity.kt b/vector/src/main/java/im/vector/riotx/features/MainActivity.kt index 13120335ea..1a42011d7e 100644 --- a/vector/src/main/java/im/vector/riotx/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/MainActivity.kt @@ -169,9 +169,14 @@ class MainActivity : VectorBaseActivity() { // the homeserver has invalidated the token (password changed, device deleted, other security reason SignedOutActivity.newIntent(this) sessionHolder.hasActiveSession() -> - // We have a session. In case of soft logout (i.e. restart of the app after a soft logout) - // the app will try to sync and will reenter the soft logout use case - HomeActivity.newIntent(this) + // We have a session. + // Check it can be opened + if(sessionHolder.getActiveSession().isOpenable) { + HomeActivity.newIntent(this) + } else { + // The token is still invalid + SoftLogoutActivity.newIntent(this) + } else -> // First start, or no active session LoginActivity.newIntent(this, null)