Skip to content

Commit

Permalink
Check if server supports qr code login.
Browse files Browse the repository at this point in the history
  • Loading branch information
Onuray Sahin committed Oct 13, 2022
1 parent 5763f5e commit e1a8226
Show file tree
Hide file tree
Showing 15 changed files with 119 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,10 @@ interface AuthenticationService {
initialDeviceName: String,
deviceId: String? = null
): Session

/**
* @param homeServerConnectionConfig the information about the homeserver and other configuration
* Return true if qr code login is supported by the server, false otherwise.
*/
suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ data class HomeServerCapabilities(
/**
* True if the home server supports controlling the logout of all devices when changing password.
*/
val canControlLogoutDevices: Boolean = false
val canControlLogoutDevices: Boolean = false,

/**
* True if the home server supports login via qr code, false otherwise.
*/
val canLoginWithQrCode: Boolean = false,
) {

enum class RoomCapabilitySupport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.auth.login.LoginWizard
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixIdFailure
import org.matrix.android.sdk.api.session.Session
Expand All @@ -42,6 +43,7 @@ import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
import org.matrix.android.sdk.internal.auth.version.Versions
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk
import org.matrix.android.sdk.internal.di.Unauthenticated
Expand Down Expand Up @@ -404,6 +406,20 @@ internal class DefaultAuthenticationService @Inject constructor(
)
}

override suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean {
val authAPI = buildAuthAPI(homeServerConnectionConfig)
val versions = runCatching {
executeRequest(null) {
authAPI.versions()
}
}
return if (versions.isSuccess) {
versions.getOrNull()?.doesServerSupportQrCodeLogin().orFalse()
} else {
false
}
}

private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
return retrofit.create(AuthAPI::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token"
private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"

/**
* Return true if the SDK supports this homeserver version.
Expand All @@ -78,6 +79,10 @@ internal fun Versions.doesServerSupportThreads(): Boolean {
return unstableFeatures?.get(FEATURE_THREADS_MSC3440_STABLE) ?: false
}

internal fun Versions.doesServerSupportQrCodeLogin(): Boolean {
return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false
}

/**
* Return true if the server support the lazy loading of room members.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject
Expand All @@ -62,7 +63,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer
) : MatrixRealmMigration(
dbName = "Session",
schemaVersion = 37L,
schemaVersion = 38L,
) {
/**
* Forces all RealmSessionStoreMigration instances to be equal.
Expand Down Expand Up @@ -109,5 +110,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform()
if (oldVersion < 37) MigrateSessionTo037(realm).perform()
if (oldVersion < 38) MigrateSessionTo038(realm).perform()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ internal object HomeServerCapabilitiesMapper {
defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
roomVersions = mapRoomVersion(entity.roomVersionsJson),
canUseThreading = entity.canUseThreading,
canControlLogoutDevices = entity.canControlLogoutDevices
canControlLogoutDevices = entity.canControlLogoutDevices,
canLoginWithQrCode = entity.canLoginWithQrCode,
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.matrix.android.sdk.internal.database.migration

import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
import org.matrix.android.sdk.internal.util.database.RealmMigrator

internal class MigrateSessionTo038(realm: DynamicRealm) : RealmMigrator(realm, 38) {

override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("HomeServerCapabilitiesEntity")
?.addField(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, Boolean::class.java)
?.transform { obj ->
obj.set(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, false)
}
?.forceRefreshOfHomeServerCapabilities()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ internal open class HomeServerCapabilitiesEntity(
var defaultIdentityServerUrl: String? = null,
var lastUpdatedTimestamp: Long = 0L,
var canUseThreading: Boolean = false,
var canControlLogoutDevices: Boolean = false
var canControlLogoutDevices: Boolean = false,
var canLoginWithQrCode: Boolean = false,
) : RealmObject() {

companion object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.extensions.orTrue
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.auth.version.Versions
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
Expand Down Expand Up @@ -134,6 +135,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
}
homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
getVersionResult?.doesServerSupportThreads().orFalse()
homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult?.doesServerSupportQrCodeLogin().orFalse()
}

if (getMediaConfigResult != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.platform.SimpleFragmentActivity
import org.matrix.android.sdk.api.extensions.orFalse
import timber.log.Timber
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.rendezvous.Rendezvous
import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
import timber.log.Timber
Expand Down Expand Up @@ -118,7 +117,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
// }
}


private fun onFailed(reason: RendezvousFailureReason) {
setState {
copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@ class OnboardingViewModel @AssistedInject constructor(
}
}

private fun observeQrCodeLoginCapability() = viewModelScope.launch {
if (!vectorFeatures.isQrCodeLoginEnabled()) {
setState {
copy(
canLoginWithQrCode = false
)
}
} else {
homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
setState {
copy(
canLoginWithQrCode = canLoginWithQrCode
)
}
}
}
}

private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
private val defaultHomeserverUrl = matrixOrgUrl

Expand Down Expand Up @@ -234,6 +253,7 @@ class OnboardingViewModel @AssistedInject constructor(
private fun handleSplashAction(action: OnboardingAction.SplashAction) {
setState { copy(onboardingFlow = action.onboardingFlow) }
continueToPageAfterSplash(action.onboardingFlow)
observeQrCodeLoginCapability()
}

private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ data class OnboardingViewState(
val selectedAuthenticationState: SelectedAuthenticationState = SelectedAuthenticationState(),

@PersistState
val personalizationState: PersonalizationState = PersonalizationState()
val personalizationState: PersonalizationState = PersonalizationState(),

val canLoginWithQrCode: Boolean = false,
) : MavericksState

enum class OnboardingFlow {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,15 @@ class FtueAuthCombinedLoginFragment :
viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
}
views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
if (vectorFeatures.isQrCodeLoginEnabled()) {

viewModel.onEach(OnboardingViewState::canLoginWithQrCode) {
configureQrCodeLoginButtonVisibility(it)
}
}

private fun configureQrCodeLoginButtonVisibility(canLoginWithQrCode: Boolean) {
if (canLoginWithQrCode) {
views.loginWithQrCode.isVisible = true
views.loginWithQrCode.debouncedClicks {
navigator
.openLoginWithQrCode(
Expand Down
15 changes: 11 additions & 4 deletions vector/src/main/res/layout/fragment_settings_devices.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

Expand Down Expand Up @@ -112,12 +113,14 @@
android:id="@+id/deviceListHeaderSignInWithQrCode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions"
app:sessionsListHeaderHasLearnMoreLink="false"
app:sessionsListHeaderDescription="@string/device_manager_sessions_sign_in_with_qr_code_description"
app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title" />
app:sessionsListHeaderHasLearnMoreLink="false"
app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title"
tools:visibility="visible" />

<Button
android:id="@+id/deviceListHeaderScanQrCodeButton"
Expand All @@ -126,9 +129,11 @@
android:layout_marginHorizontal="16dp"
android:layout_marginTop="12dp"
android:text="@string/qr_code_login_scan_qr_code_button"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode" />
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode"
tools:visibility="visible" />

<Button
android:id="@+id/deviceListHeaderShowQrCodeButton"
Expand All @@ -139,9 +144,11 @@
android:layout_marginTop="4dp"
android:layout_marginBottom="12dp"
android:text="@string/qr_code_login_show_qr_code_button"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton" />
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton"
tools:visibility="visible" />

<include
android:id="@+id/waiting_view"
Expand Down

0 comments on commit e1a8226

Please sign in to comment.