diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt
index 659d26610cc3eaadfff26f2bef8c0b65ceb091ef..a6f945892fa598ad8c03e06774421ef86e344e52 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt
@@ -98,7 +98,7 @@ class LogInFragment : Fragment(R.layout.fragment_log_in), HasLoadingState {
         NetworkObserver.observe(this) { setEnabledViews(it) }
         viewModel.loginResultLiveData.observeResponse(this,
             success = {
-                findNavController().navigateSafe(LogInFragmentDirections.toLoginStagesFragment())
+                findNavController().navigateSafe(LogInFragmentDirections.toUiaFragment())
             },
             error = {
                 showError(getString(R.string.username_not_found))
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LoginDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LoginDataSource.kt
index e404b11f9ecb02467923096b01e59f1bde3172e8..88d4aff1fbd4be7e2bef2d5e78a287d291f85dde 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LoginDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LoginDataSource.kt
@@ -24,7 +24,7 @@ class LoginDataSource @Inject constructor(
     private val uiaFactory: UIADataSource.Factory
 ) {
 
-    private val loginStagesDataSource by lazy { uiaFactory.create(UIAFlowType.Login) }
+    private val uiaDataSource by lazy { uiaFactory.create(UIAFlowType.Login) }
     private val authService by lazy { MatrixInstanceProvider.matrix.authenticationService() }
 
     suspend fun startLogin(
@@ -34,7 +34,7 @@ class LoginDataSource @Inject constructor(
     ) = createResult {
         authService.cancelPendingLoginOrRegistration()
         val stages = prepareLoginStages(userName, domain, isForgotPassword)
-        loginStagesDataSource.startUIAStages(stages, domain, userName)
+        uiaDataSource.startUIAStages(stages, domain, userName)
     }
 
     private suspend fun prepareLoginStages(
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/reauth/AuthConfirmationProvider.kt b/auth/src/main/java/org/futo/circles/auth/feature/reauth/AuthConfirmationProvider.kt
index 4e83420c6c358204739b5a791f22e6d2b6d87817..61c77565df6c41376950e1bc208846cca7047bd5 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/reauth/AuthConfirmationProvider.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/reauth/AuthConfirmationProvider.kt
@@ -1,5 +1,10 @@
 package org.futo.circles.auth.feature.reauth
 
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.launch
+import org.futo.circles.auth.feature.uia.UIADataSource
+import org.futo.circles.auth.feature.uia.UIAFlowType
 import org.futo.circles.core.base.SingleEventLiveData
 import org.matrix.android.sdk.api.auth.UIABaseAuth
 import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
@@ -10,10 +15,11 @@ import kotlin.coroutines.Continuation
 import kotlin.coroutines.resumeWithException
 
 class AuthConfirmationProvider @Inject constructor(
-    private val reAuthStagesDataSource: ReAuthStagesDataSource
+    private val uiaFactory: UIADataSource.Factory
 ) : UserInteractiveAuthInterceptor {
 
     val startReAuthEventLiveData = SingleEventLiveData<Unit>()
+    private val uiaDataSource by lazy { uiaFactory.create(UIAFlowType.ReAuth) }
 
     override fun performStage(
         flowResponse: RegistrationFlowResponse,
@@ -24,9 +30,11 @@ class AuthConfirmationProvider @Inject constructor(
         if (flowResponse.completedStages.isNullOrEmpty()) {
             val stages = flowResponse.toFlowsWithStages().firstOrNull() ?: emptyList()
             startReAuthEventLiveData.postValue(Unit)
-            reAuthStagesDataSource.startReAuthStages(flowResponse.session ?: "", stages, promise)
+            MainScope().launch(Dispatchers.IO) {
+                uiaDataSource.startUIAStages(stages, flowResponse.session ?: "", promise)
+            }
         } else {
-            reAuthStagesDataSource.onStageResult(promise, flowResponse, errCode)
+            uiaDataSource.onStageResult(promise, flowResponse, errCode)
         }
     }
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeDataSource.kt
index 6a2ebdc000c35aabb12f3f3b4ebbe49673e79645..9d6a1596e66668a2acbce69bbe192d1afccc8538 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeDataSource.kt
@@ -3,9 +3,11 @@ package org.futo.circles.auth.feature.sign_up.sign_up_type
 import android.content.Context
 import dagger.hilt.android.qualifiers.ApplicationContext
 import org.futo.circles.auth.R
+import org.futo.circles.auth.feature.uia.UIADataSource
 import org.futo.circles.auth.feature.uia.UIADataSource.Companion.REGISTRATION_FREE_TYPE
 import org.futo.circles.auth.feature.uia.UIADataSource.Companion.REGISTRATION_SUBSCRIPTION_TYPE
 import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.auth.feature.uia.UIAFlowType
 import org.futo.circles.core.base.CirclesAppConfig
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.provider.MatrixInstanceProvider
@@ -14,12 +16,13 @@ import org.matrix.android.sdk.api.auth.registration.Stage
 import javax.inject.Inject
 
 class SelectSignUpTypeDataSource @Inject constructor(
-    @ApplicationContext private val context: Context
+    @ApplicationContext private val context: Context,
+    private val uiaFactory: UIADataSource.Factory
 ) {
 
     private var registrationFlowsForDomain: Pair<String, List<List<Stage>>>? = null
 
-    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+    private val uiaDataSource by lazy { uiaFactory.create(UIAFlowType.Signup) }
 
     suspend fun getAuthFlowsFor(domain: String) = createResult {
         registrationFlowsForDomain = null
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/LoginStagesDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/LoginStagesDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ba31c984328b5b43ef24e91d27214a0f27b09d8c
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/LoginStagesDataSource.kt
@@ -0,0 +1,144 @@
+package org.futo.circles.auth.feature.uia.flow
+
+import android.content.Context
+import android.net.Uri
+import dagger.hilt.android.qualifiers.ApplicationContext
+import org.futo.circles.auth.R
+import org.futo.circles.auth.bsspeke.BSSpekeClientProvider
+import org.futo.circles.auth.feature.pass_phrase.EncryptionAlgorithmHelper
+import org.futo.circles.auth.feature.pass_phrase.create.CreatePassPhraseDataSource
+import org.futo.circles.auth.feature.pass_phrase.restore.RestoreBackupDataSource
+import org.futo.circles.auth.feature.token.RefreshTokenManager
+import org.futo.circles.auth.feature.uia.UIADataSource
+import org.futo.circles.core.base.SingleEventLiveData
+import org.futo.circles.core.extensions.Response
+import org.futo.circles.core.extensions.createResult
+import org.futo.circles.core.model.LoadingData
+import org.futo.circles.core.provider.MatrixInstanceProvider
+import org.futo.circles.core.provider.MatrixSessionProvider
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import org.matrix.android.sdk.api.auth.registration.Stage
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.util.JsonDict
+import javax.inject.Inject
+import javax.inject.Singleton
+
+enum class LoginNavigationEvent { Main, PassPhrase }
+
+@Singleton
+class LoginStagesDataSource @Inject constructor(
+    @ApplicationContext private val context: Context,
+    private val restoreBackupDataSource: RestoreBackupDataSource,
+    private val encryptionAlgorithmHelper: EncryptionAlgorithmHelper,
+    private val createPassPhraseDataSource: CreatePassPhraseDataSource,
+    private val refreshTokenManager: RefreshTokenManager
+) : UIADataSource(context) {
+
+    val loginNavigationLiveData = SingleEventLiveData<LoginNavigationEvent>()
+    val passPhraseLoadingLiveData = restoreBackupDataSource.loadingLiveData
+
+    private var userPassword: String = ""
+
+    override suspend fun startUIAStages(
+        stages: List<Stage>,
+        serverDomain: String,
+        name: String?
+    ) {
+        userPassword = ""
+        name ?: throw IllegalArgumentException("Username is required for login")
+        super.startUIAStages(stages, serverDomain, name)
+    }
+
+    override suspend fun performUIAStage(
+        authParams: JsonDict,
+        name: String?,
+        password: String?
+    ): Response<RegistrationResult> {
+        val wizard = MatrixInstanceProvider.matrix.authenticationService().getLoginWizard()
+        val result = createResult {
+            wizard.loginStageCustom(
+                authParams,
+                getIdentifier(),
+                context.getString(R.string.initial_device_name),
+                true
+            )
+        }
+        (result as? Response.Success)?.let {
+            password?.let { userPassword = it }
+            stageCompleted(result.data)
+        }
+        return result
+    }
+
+
+    override suspend fun finishStages(session: Session) {
+        passPhraseLoadingLiveData.postValue(
+            LoadingData(messageId = R.string.initial_sync, isLoading = true)
+        )
+        MatrixSessionProvider.awaitForSessionSync(session)
+        passPhraseLoadingLiveData.postValue(LoadingData(isLoading = false))
+        refreshTokenManager.scheduleTokenRefreshIfNeeded(session)
+        handleKeysBackup()
+        BSSpekeClientProvider.clear()
+    }
+
+    private fun getIdentifier() = mapOf(
+        USER_PARAM_KEY to "@$userName:$domain",
+        TYPE_PARAM_KEY to LOGIN_PASSWORD_USER_ID_TYPE
+    )
+
+    private suspend fun handleKeysBackup() {
+        if (encryptionAlgorithmHelper.isBcryptAlgorithm()) restoreAndMigrateBCrypt(userPassword)
+        else {
+            if (encryptionAlgorithmHelper.isBsSpekePassPhrase()) restoreBsSpekeBackup()
+            else loginNavigationLiveData.postValue(LoginNavigationEvent.PassPhrase)
+        }
+    }
+
+    private suspend fun restoreBsSpekeBackup(): Response<Unit> {
+        val restoreResult = createResult { restoreBackupDataSource.restoreWithBsSpekeKey() }
+        return handleRestoreResult(restoreResult)
+    }
+
+    private suspend fun restoreAndMigrateBCrypt(passphrase: String): Response<Unit> {
+        val restoreResult = createResult {
+            restoreBackupDataSource.restoreBcryptWithPassPhase(passphrase)
+            createPassPhraseDataSource.replaceToNewKeyBackup()
+        }
+        return handleRestoreResult(restoreResult)
+    }
+
+
+    suspend fun restoreBackupWithPassphrase(password: String): Response<Unit> {
+        val restoreResult = createResult {
+            restoreBackupDataSource.restoreKeysWithPassPhase(password)
+        }
+        return handleRestoreResult(restoreResult)
+    }
+
+    suspend fun restoreBackupWithRawKey(rawKey: String): Response<Unit> {
+        val restoreResult = createResult {
+            restoreBackupDataSource.restoreKeysWithRawKey(rawKey)
+        }
+        return handleRestoreResult(restoreResult)
+    }
+
+    suspend fun restoreBackup(uri: Uri): Response<Unit> {
+        val restoreResult = createResult {
+            restoreBackupDataSource.restoreKeysWithRecoveryKey(uri)
+        }
+        return handleRestoreResult(restoreResult)
+    }
+
+    private fun handleRestoreResult(restoreResult: Response<Unit>): Response<Unit> {
+        when (restoreResult) {
+            is Response.Error -> loginNavigationLiveData.postValue(LoginNavigationEvent.PassPhrase)
+            is Response.Success -> navigateToMain()
+        }
+        return restoreResult
+    }
+
+    fun navigateToMain() {
+        loginNavigationLiveData.postValue(LoginNavigationEvent.Main)
+    }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/model/CustomUIAuth.kt b/auth/src/main/java/org/futo/circles/auth/model/CustomUIAuth.kt
index fd30b78f677f962f6c7127ff60a7eeb70b5d374a..098c3cafac5bed5f82b24707d344687f73417ffb 100644
--- a/auth/src/main/java/org/futo/circles/auth/model/CustomUIAuth.kt
+++ b/auth/src/main/java/org/futo/circles/auth/model/CustomUIAuth.kt
@@ -1,5 +1,6 @@
 package org.futo.circles.auth.model
 
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.USER_PARAM_KEY
 import org.futo.circles.core.provider.MatrixSessionProvider
 import org.matrix.android.sdk.api.auth.UIABaseAuth
 import org.matrix.android.sdk.api.util.JsonDict
@@ -14,8 +15,7 @@ data class CustomUIAuth(
 
     override fun asMap(): Map<String, *> = auth.toMutableMap().apply {
         this["session"] = session
-        this[BaseLoginStagesDataSource.USER_PARAM_KEY] =
-            MatrixSessionProvider.currentSession?.myUserId ?: ""
+        this[USER_PARAM_KEY] = MatrixSessionProvider.currentSession?.myUserId ?: ""
     }
 
 }