Skip to content
Merged
1 change: 1 addition & 0 deletions changelog.d/5721.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improving deactivation experience along with a crash fix
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
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.internal.util.exceptions.UiaCancelledException
import timber.log.Timber
import kotlin.coroutines.suspendCoroutine

Expand All @@ -30,14 +31,15 @@ import kotlin.coroutines.suspendCoroutine
* @param interceptor see doc in [UserInteractiveAuthInterceptor]
* @param retryBlock called at the end of the process, in this block generally retry executing the task, with
* provided authUpdate
* @return true if UIA is handled without error
* @return UiaResult if UIA handled, failed or cancelled
*
*/
internal suspend fun handleUIA(failure: Throwable,
interceptor: UserInteractiveAuthInterceptor,
retryBlock: suspend (UIABaseAuth) -> Unit): Boolean {
retryBlock: suspend (UIABaseAuth) -> Unit): UiaResult {
Timber.d("## UIA: check error ${failure.message}")
val flowResponse = failure.toRegistrationFlowResponse()
?: return false.also {
?: return UiaResult.FAILURE.also {
Timber.d("## UIA: not a UIA error")
}

Expand All @@ -50,14 +52,19 @@ internal suspend fun handleUIA(failure: Throwable,
interceptor.performStage(flowResponse, (failure as? Failure.ServerError)?.error?.code, continuation)
}
} catch (failure2: Throwable) {
Timber.w(failure2, "## UIA: failed to participate")
return false
return if (failure2 is UiaCancelledException) {
Timber.w(failure2, "## UIA: cancelled")
UiaResult.CANCELLED
} else {
Timber.w(failure2, "## UIA: failed to participate")
UiaResult.FAILURE
}
}

Timber.d("## UIA: updated auth")
return try {
retryBlock(authUpdate)
true
UiaResult.SUCCESS
} catch (failure3: Throwable) {
handleUIA(failure3, interceptor, retryBlock)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 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.auth.registration

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move it to api package? If you merge develop into your branch, there is now a uia sub-package

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


enum class UiaResult {
SUCCESS,
FAILURE,
CANCELLED
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.tasks

import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.internal.auth.registration.UiaResult
import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.api.CryptoApi
import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams
Expand Down Expand Up @@ -47,13 +48,13 @@ internal class DefaultDeleteDeviceTask @Inject constructor(
}
} catch (throwable: Throwable) {
if (params.userInteractiveAuthInterceptor == null ||
!handleUIA(
handleUIA(
failure = throwable,
interceptor = params.userInteractiveAuthInterceptor,
retryBlock = { authUpdate ->
execute(params.copy(userAuthParam = authUpdate))
}
)
) != UiaResult.SUCCESS
) {
Timber.d("## UIA: propagate failure")
throw throwable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.crypto.tasks

import dagger.Lazy
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.internal.auth.registration.UiaResult
import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
import org.matrix.android.sdk.internal.crypto.MyDeviceInfoHolder
Expand Down Expand Up @@ -126,13 +127,13 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor(
uploadSigningKeysTask.execute(uploadSigningKeysParams)
} catch (failure: Throwable) {
if (params.interactiveAuthInterceptor == null ||
!handleUIA(
handleUIA(
failure = failure,
interceptor = params.interactiveAuthInterceptor,
retryBlock = { authUpdate ->
uploadSigningKeysTask.execute(uploadSigningKeysParams.copy(userAuthParam = authUpdate))
}
)
) != UiaResult.SUCCESS
) {
Timber.d("## UIA: propagate failure")
throw failure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ package org.matrix.android.sdk.internal.session.account

import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.internal.auth.registration.UiaResult
import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.cleanup.CleanupSession
import org.matrix.android.sdk.internal.session.identity.IdentityDisconnectTask
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.exceptions.UiaCancelledException
import timber.log.Timber
import javax.inject.Inject

Expand Down Expand Up @@ -51,18 +53,24 @@ internal class DefaultDeactivateAccountTask @Inject constructor(
}
true
} catch (throwable: Throwable) {
if (!handleUIA(
failure = throwable,
interceptor = params.userInteractiveAuthInterceptor,
retryBlock = { authUpdate ->
execute(params.copy(userAuthParam = authUpdate))
}
)
) {
Timber.d("## UIA: propagate failure")
throw throwable
} else {
false
when (handleUIA(
failure = throwable,
interceptor = params.userInteractiveAuthInterceptor,
retryBlock = { authUpdate ->
execute(params.copy(userAuthParam = authUpdate))
}
)) {
UiaResult.SUCCESS -> {
false
}
UiaResult.FAILURE -> {
Timber.d("## UIA: propagate failure")
throw throwable
}
UiaResult.CANCELLED -> {
Timber.d("## UIA: cancelled")
throw UiaCancelledException()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ 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.auth.registration.UiaResult
import org.matrix.android.sdk.internal.auth.registration.handleUIA
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
Expand Down Expand Up @@ -72,13 +73,13 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
true
} catch (throwable: Throwable) {
if (params.userInteractiveAuthInterceptor == null ||
!handleUIA(
handleUIA(
failure = throwable,
interceptor = params.userInteractiveAuthInterceptor,
retryBlock = { authUpdate ->
execute(params.copy(userAuthParam = authUpdate))
}
)
) != UiaResult.SUCCESS
) {
Timber.d("## UIA: propagate failure")
throw throwable.toRegistrationFlowResponse()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 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.util.exceptions

class UiaCancelledException(message: String? = null) : Exception(message)
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.settings.VectorSettingsActivity
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.internal.util.exceptions.UiaCancelledException
import javax.inject.Inject

class DeactivateAccountFragment @Inject constructor() : VectorBaseFragment<FragmentDeactivateAccountBinding>() {
Expand Down Expand Up @@ -114,7 +115,9 @@ class DeactivateAccountFragment @Inject constructor() : VectorBaseFragment<Fragm
is DeactivateAccountViewEvents.OtherFailure -> {
settingsActivity?.ignoreInvalidTokenError = false
dismissLoadingDialog()
displayErrorDialog(it.throwable)
if (it.throwable !is UiaCancelledException) {
displayErrorDialog(it.throwable)
}
}
DeactivateAccountViewEvents.Done -> {
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCredentials = true, isAccountDeactivated = true))
Expand All @@ -123,7 +126,8 @@ class DeactivateAccountFragment @Inject constructor() : VectorBaseFragment<Fragm
ReAuthActivity.newIntent(requireContext(),
it.registrationFlowResponse,
it.lastErrorCode,
getString(R.string.deactivate_account_title)).let { intent ->
getString(R.string.deactivate_account_title)
).let { intent ->
reAuthActivityResultLauncher.launch(intent)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.failure.isInvalidUIAAuth
import org.matrix.android.sdk.api.session.Session
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.util.exceptions.UiaCancelledException
import timber.log.Timber
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
Expand All @@ -44,7 +45,7 @@ data class DeactivateAccountViewState(

class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private val initialState: DeactivateAccountViewState,
private val session: Session) :
VectorViewModel<DeactivateAccountViewState, DeactivateAccountAction, DeactivateAccountViewEvents>(initialState) {
VectorViewModel<DeactivateAccountViewState, DeactivateAccountAction, DeactivateAccountViewEvents>(initialState) {

@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<DeactivateAccountViewModel, DeactivateAccountViewState> {
Expand All @@ -57,15 +58,17 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
override fun handle(action: DeactivateAccountAction) {
when (action) {
is DeactivateAccountAction.DeactivateAccount -> handleDeactivateAccount(action)
DeactivateAccountAction.SsoAuthDone -> {
DeactivateAccountAction.SsoAuthDone -> {
Timber.d("## UIA - FallBack success")
_viewEvents.post(DeactivateAccountViewEvents.Loading())
if (pendingAuth != null) {
uiaContinuation?.resume(pendingAuth!!)
} else {
uiaContinuation?.resumeWithException(IllegalArgumentException())
}
}
is DeactivateAccountAction.PasswordAuthDone -> {
is DeactivateAccountAction.PasswordAuthDone -> {
_viewEvents.post(DeactivateAccountViewEvents.Loading())
val decryptedPass = session.loadSecureSecret<String>(action.password.fromBase64().inputStream(), ReAuthActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)
uiaContinuation?.resume(
UserPasswordAuth(
Expand All @@ -75,9 +78,9 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
)
)
}
DeactivateAccountAction.ReAuthCancelled -> {
DeactivateAccountAction.ReAuthCancelled -> {
Timber.d("## UIA - Reauth cancelled")
uiaContinuation?.resumeWithException(Exception())
uiaContinuation?.resumeWithException(UiaCancelledException())
uiaContinuation = null
pendingAuth = null
}
Expand All @@ -100,7 +103,7 @@ class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private v
}
)
DeactivateAccountViewEvents.Done
} catch (failure: Exception) {
} catch (failure: Throwable) {
if (failure.isInvalidUIAAuth()) {
DeactivateAccountViewEvents.InvalidAuth
} else {
Expand Down