SSO UIA for add 3pid + refactoring move RegistrationFlow to api

This commit is contained in:
Valere 2021-02-02 22:25:49 +01:00
parent 2a3962265b
commit 0211197c47
28 changed files with 164 additions and 121 deletions

View File

@ -16,7 +16,7 @@
package org.matrix.android.sdk.api.auth
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth
import kotlin.coroutines.Continuation

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.matrix.android.sdk.internal.auth.registration
package org.matrix.android.sdk.api.auth.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@ -109,3 +109,9 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
return FlowResult(missingStage, completedStage)
}
fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? {
val completed = completedStages ?: emptyList()
return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() }
}

View File

@ -16,8 +16,8 @@
package org.matrix.android.sdk.api.failure
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.di.MoshiProvider
import java.io.IOException
import javax.net.ssl.HttpsURLConnection
@ -59,7 +59,7 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? {
.adapter(RegistrationFlowResponse::class.java)
.fromJson(this.errorBody)
}
} else if (this is Failure.ServerError && this.error.code == MatrixError.M_FORBIDDEN) {
} else if (this is Failure.ServerError && this.httpCode == 401 && this.error.code == MatrixError.M_FORBIDDEN) {
// This happens when the submission for this stage was bad (like bad password)
if (this.error.session != null && this.error.flows != null) {
RegistrationFlowResponse(

View File

@ -16,8 +16,8 @@
package org.matrix.android.sdk.api.failure
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
import java.io.IOException

View File

@ -20,6 +20,7 @@ package org.matrix.android.sdk.api.session.profile
import android.net.Uri
import androidx.lifecycle.LiveData
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.JsonDict
@ -107,8 +108,7 @@ interface ProfileService {
* Finalize adding a 3Pids. Call this method once the user has validated that he owns the ThreePid
*/
fun finalizeAddingThreePid(threePid: ThreePid,
uiaSession: String?,
accountPassword: String?,
userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
matrixCallback: MatrixCallback<Unit>): Cancelable
/**

View File

@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
import org.matrix.android.sdk.api.auth.registration.toFlowResult
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError
import org.matrix.android.sdk.api.util.Cancelable

View File

@ -23,11 +23,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth
import timber.log.Timber
import kotlin.coroutines.suspendCoroutine
fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? {
val completed = completedStages ?: emptyList()
return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() }
}
suspend fun handleUIA(failure: Throwable, interceptor: UserInteractiveAuthInterceptor, retryBlock: suspend (UIABaseAuth) -> Unit): Boolean {
Timber.d("## UIA: check error ${failure.message}")
val flowResponse = failure.toRegistrationFlowResponse()

View File

@ -22,6 +22,7 @@ import androidx.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy
import io.realm.kotlin.where
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.profile.ProfileService
import org.matrix.android.sdk.api.util.Cancelable
@ -170,14 +171,12 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
}
override fun finalizeAddingThreePid(threePid: ThreePid,
uiaSession: String?,
accountPassword: String?,
userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor,
matrixCallback: MatrixCallback<Unit>): Cancelable {
return finalizeAddingThreePidTask
.configureWith(FinalizeAddingThreePidTask.Params(
threePid = threePid,
session = uiaSession,
accountPassword = accountPassword,
userInteractiveAuthInterceptor = userInteractiveAuthInterceptor,
userWantsToCancel = false
)) {
callback = alsoRefresh(matrixCallback)
@ -189,8 +188,7 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
return finalizeAddingThreePidTask
.configureWith(FinalizeAddingThreePidTask.Params(
threePid = threePid,
session = null,
accountPassword = null,
userInteractiveAuthInterceptor = null,
userWantsToCancel = true
)) {
callback = alsoRefresh(matrixCallback)

View File

@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.profile
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
@JsonClass(generateAdapter = true)
internal data class FinalizeAddThreePidBody(
@ -37,5 +36,5 @@ internal data class FinalizeAddThreePidBody(
* Additional authentication information for the user-interactive authentication API.
*/
@Json(name = "auth")
val auth: UserPasswordAuth?
val auth: Map<String, *>? = null
)

View File

@ -17,10 +17,12 @@
package org.matrix.android.sdk.internal.session.profile
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
import org.matrix.android.sdk.internal.di.SessionDatabase
@ -29,13 +31,14 @@ import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction
import timber.log.Timber
import javax.inject.Inject
internal abstract class FinalizeAddingThreePidTask : Task<FinalizeAddingThreePidTask.Params, Unit> {
data class Params(
val threePid: ThreePid,
val session: String?,
val accountPassword: String?,
val userInteractiveAuthInterceptor: UserInteractiveAuthInterceptor?,
val userAuthParam: UIABaseAuth? = null,
val userWantsToCancel: Boolean
)
}
@ -62,20 +65,21 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
val body = FinalizeAddThreePidBody(
clientSecret = pendingThreePids.clientSecret,
sid = pendingThreePids.sid,
auth = if (params.session != null && params.accountPassword != null) {
UserPasswordAuth(
session = params.session,
user = userId,
password = params.accountPassword
)
} else null
auth = params.userAuthParam?.asMap()
)
apiCall = profileAPI.finalizeAddThreePid(body)
}
} catch (throwable: Throwable) {
throw throwable.toRegistrationFlowResponse()
?.let { Failure.RegistrationFlowError(it) }
?: throwable
if (params.userInteractiveAuthInterceptor == null
|| !handleUIA(throwable, params.userInteractiveAuthInterceptor) { auth ->
execute(params.copy(userAuthParam = auth))
}
) {
Timber.d("## UIA: propagate failure")
throw throwable.toRegistrationFlowResponse()
?.let { Failure.RegistrationFlowError(it) }
?: throwable
}
}
}

View File

@ -105,11 +105,13 @@ class DefaultErrorFormatter @Inject constructor(
HttpURLConnection.HTTP_NOT_FOUND ->
// homeserver not found
stringProvider.getString(R.string.login_error_no_homeserver_found)
HttpURLConnection.HTTP_UNAUTHORIZED ->
// uia errors?
stringProvider.getString(R.string.error_unauthorized)
else ->
throwable.localizedMessage
}
}
is SsoFlowNotSupportedYet -> stringProvider.getString(R.string.error_sso_flow_not_supported_yet)
else -> throwable.localizedMessage
}
?: stringProvider.getString(R.string.unknown_error)

View File

@ -1,19 +0,0 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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 im.vector.app.core.error
class SsoFlowNotSupportedYet : Throwable()

View File

@ -200,6 +200,7 @@ abstract class VectorBaseFragment<VB: ViewBinding> : BaseMvRxFragment(), HasScre
}
protected fun showLoadingDialog(message: CharSequence? = null, cancelable: Boolean = false) {
progress?.dismiss()
progress = ProgressDialog(requireContext()).apply {
setCancelable(cancelable)
setMessage(message ?: getString(R.string.please_wait))

View File

@ -37,8 +37,8 @@ import im.vector.app.core.utils.openUrlInChromeCustomTab
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.auth.AuthenticationService
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import timber.log.Timber
import javax.inject.Inject

View File

@ -43,8 +43,8 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult
import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey

View File

@ -17,7 +17,7 @@
package im.vector.app.features.crypto.recover
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
sealed class BootstrapViewEvents : VectorViewEvents {
data class Dismiss(val success: Boolean) : BootstrapViewEvents()

View File

@ -40,8 +40,8 @@ import org.matrix.android.sdk.api.session.InitialSyncProgressService
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth

View File

@ -17,7 +17,7 @@
package im.vector.app.features.settings.account.deactivation
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
/**
* Transient events for deactivate account settings screen

View File

@ -30,7 +30,7 @@ import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.failure.isInvalidUIAAuth
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth

View File

@ -17,7 +17,7 @@
package im.vector.app.features.settings.crosssigning
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
/**
* Transient events for cross signing settings screen

View File

@ -37,8 +37,8 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified
import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth

View File

@ -19,7 +19,7 @@ package im.vector.app.features.settings.devices
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo

View File

@ -49,8 +49,8 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.internal.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.internal.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo

View File

@ -25,6 +25,11 @@ sealed class ThreePidsSettingsAction : VectorViewModelAction {
data class SubmitCode(val threePid: ThreePid.Msisdn, val code: String) : ThreePidsSettingsAction()
data class ContinueThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
data class CancelThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
data class AccountPassword(val password: String) : ThreePidsSettingsAction()
// data class AccountPassword(val password: String) : ThreePidsSettingsAction()
data class DeleteThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
object SsoAuthDone : ThreePidsSettingsAction()
data class PasswordAuthDone(val password: String) : ThreePidsSettingsAction()
object ReAuthCancelled : ThreePidsSettingsAction()
}

View File

@ -16,6 +16,7 @@
package im.vector.app.features.settings.threepids
import android.app.Activity
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
@ -26,7 +27,6 @@ import androidx.appcompat.app.AppCompatActivity
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.dialogs.PromptPasswordDialog
import im.vector.app.core.dialogs.withColoredButton
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
@ -35,10 +35,12 @@ import im.vector.app.core.extensions.getFormattedValue
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.isEmail
import im.vector.app.core.extensions.isMsisdn
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.auth.ReAuthActivity
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.session.identity.ThreePid
import javax.inject.Inject
@ -64,15 +66,42 @@ class ThreePidsSettingsFragment @Inject constructor(
viewModel.observeViewEvents {
when (it) {
is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable)
ThreePidsSettingsViewEvents.RequestPassword -> askUserPassword()
is ThreePidsSettingsViewEvents.Failure -> displayErrorDialog(it.throwable)
is ThreePidsSettingsViewEvents.RequestReAuth -> askAuthentication(it)
}.exhaustive
}
}
private fun askUserPassword() {
PromptPasswordDialog().show(requireActivity()) { password ->
viewModel.handle(ThreePidsSettingsAction.AccountPassword(password))
// private fun askUserPassword() {
// PromptPasswordDialog().show(requireActivity()) { password ->
// viewModel.handle(ThreePidsSettingsAction.AccountPassword(password))
// }
// }
private fun askAuthentication(event: ThreePidsSettingsViewEvents.RequestReAuth) {
ReAuthActivity.newIntent(requireContext(),
event.registrationFlowResponse,
event.lastErrorCode,
getString(R.string.settings_add_email_address)).let { intent ->
reAuthActivityResultLauncher.launch(intent)
}
}
private val reAuthActivityResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
when (activityResult.data?.extras?.getString(ReAuthActivity.RESULT_FLOW_TYPE)) {
LoginFlowTypes.SSO -> {
viewModel.handle(ThreePidsSettingsAction.SsoAuthDone)
}
LoginFlowTypes.PASSWORD -> {
val password = activityResult.data?.extras?.getString(ReAuthActivity.RESULT_VALUE) ?: ""
viewModel.handle(ThreePidsSettingsAction.PasswordAuthDone(password))
}
else -> {
viewModel.handle(ThreePidsSettingsAction.ReAuthCancelled)
}
}
} else {
viewModel.handle(ThreePidsSettingsAction.ReAuthCancelled)
}
}

View File

@ -17,8 +17,10 @@
package im.vector.app.features.settings.threepids
import im.vector.app.core.platform.VectorViewEvents
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
sealed class ThreePidsSettingsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : ThreePidsSettingsViewEvents()
object RequestPassword : ThreePidsSettingsViewEvents()
// object RequestPassword : ThreePidsSettingsViewEvents()
data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : ThreePidsSettingsViewEvents()
}

View File

@ -24,21 +24,28 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.error.SsoFlowNotSupportedYet
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ReadOnceTrue
import im.vector.app.features.auth.ReAuthActivity
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64
import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth
import org.matrix.android.sdk.internal.crypto.model.rest.UIABaseAuth
import org.matrix.android.sdk.internal.crypto.model.rest.UserPasswordAuth
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
class ThreePidsSettingsViewModel @AssistedInject constructor(
@Assisted initialState: ThreePidsSettingsViewState,
@ -48,36 +55,16 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
// UIA session
private var pendingThreePid: ThreePid? = null
private var pendingSession: String? = null
// private var pendingSession: String? = null
private val loadingCallback: MatrixCallback<Unit> = object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) {
isLoading(false)
if (failure is Failure.RegistrationFlowError) {
var isPasswordRequestFound = false
// We only support LoginFlowTypes.PASSWORD
// Check if we can provide the user password
failure.registrationFlowResponse.flows?.forEach { interactiveAuthenticationFlow ->
isPasswordRequestFound = isPasswordRequestFound || interactiveAuthenticationFlow.stages?.any { it == LoginFlowTypes.PASSWORD } == true
}
if (isPasswordRequestFound) {
pendingSession = failure.registrationFlowResponse.session
_viewEvents.post(ThreePidsSettingsViewEvents.RequestPassword)
} else {
// LoginFlowTypes.PASSWORD not supported, and this is the only one Element supports so far...
_viewEvents.post(ThreePidsSettingsViewEvents.Failure(SsoFlowNotSupportedYet()))
}
} else {
_viewEvents.post(ThreePidsSettingsViewEvents.Failure(failure))
}
_viewEvents.post(ThreePidsSettingsViewEvents.Failure(failure))
}
override fun onSuccess(data: Unit) {
pendingThreePid = null
pendingSession = null
isLoading(false)
}
}
@ -142,16 +129,50 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
override fun handle(action: ThreePidsSettingsAction) {
when (action) {
is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action)
is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action)
is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action)
is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action)
is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action)
is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action)
is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action)
is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action)
is ThreePidsSettingsAction.SubmitCode -> handleSubmitCode(action)
is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action)
is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action)
is ThreePidsSettingsAction.ChangeUiState -> handleChangeUiState(action)
ThreePidsSettingsAction.SsoAuthDone -> {
Timber.d("## UIA - FallBack success")
if (pendingAuth != null) {
uiaContinuation?.resume(pendingAuth!!)
} else {
uiaContinuation?.resumeWith(Result.failure((IllegalArgumentException())))
}
}
is ThreePidsSettingsAction.PasswordAuthDone -> {
val decryptedPass = session.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
session = pendingAuth?.session,
password = decryptedPass,
user = session.myUserId
)
)
}
ThreePidsSettingsAction.ReAuthCancelled -> {
Timber.d("## UIA - Reauth cancelled")
uiaContinuation?.resumeWith(Result.failure((Exception())))
uiaContinuation = null
pendingAuth = null
}
}.exhaustive
}
var uiaContinuation: Continuation<UIABaseAuth>? = null
var pendingAuth: UIABaseAuth? = null
private val uiaInterceptor = object : UserInteractiveAuthInterceptor {
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
_viewEvents.post(ThreePidsSettingsViewEvents.RequestReAuth(flowResponse, errCode))
pendingAuth = DefaultBaseAuth(session = flowResponse.session)
uiaContinuation = promise
}
}
private fun handleSubmitCode(action: ThreePidsSettingsAction.SubmitCode) {
isLoading(true)
setState {
@ -168,7 +189,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
override fun onSuccess(data: Unit) {
// then finalize
pendingThreePid = action.threePid
session.finalizeAddingThreePid(action.threePid, null, null, loadingCallback)
session.finalizeAddingThreePid(action.threePid, uiaInterceptor, loadingCallback)
}
override fun onFailure(failure: Throwable) {
@ -232,7 +253,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
isLoading(true)
pendingThreePid = action.threePid
viewModelScope.launch {
session.finalizeAddingThreePid(action.threePid, null, null, loadingCallback)
session.finalizeAddingThreePid(action.threePid, uiaInterceptor, loadingCallback)
}
}
@ -243,16 +264,14 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
}
}
private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) {
val safeSession = pendingSession ?: return Unit
.also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending session"))) }
val safeThreePid = pendingThreePid ?: return Unit
.also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending threePid"))) }
isLoading(true)
viewModelScope.launch {
session.finalizeAddingThreePid(safeThreePid, safeSession, action.password, loadingCallback)
}
}
// private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) {
// val safeThreePid = pendingThreePid ?: return Unit
// .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending threePid"))) }
// isLoading(true)
// viewModelScope.launch {
// session.finalizeAddingThreePid(safeThreePid, uiaInterceptor, loadingCallback)
// }
// }
private fun handleDeleteThreePid(action: ThreePidsSettingsAction.DeleteThreePid) {
isLoading(true)

View File

@ -333,6 +333,7 @@
<string name="login_error_ssl_handshake">Your device is using an outdated TLS security protocol, vulnerable to attack, for your security you will not be able to connect</string>
<string name="login_error_forbidden">Invalid username/password</string>
<string name="error_unauthorized">Unauthorized, missing valid authentication credentials</string>
<string name="login_error_unknown_token">The access token specified was not recognised</string>
<string name="login_error_bad_json">Malformed JSON</string>
<string name="login_error_not_json">Did not contain valid JSON</string>