diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 07ebfa45e3130f33ab5921bbc3678c811855216f..85c804c4587f45fddc8bc08560ec1313af0a6687 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -70,8 +70,6 @@ native <methods>;
 
 -keep class org.futo.circles.core.model.CircleRoomTypeArg
 -keep class org.futo.circles.core.model.InviteTypeArg
--keep class org.futo.circles.auth.model.PasswordModeArg
--keep class org.futo.circles.auth.model.TermsModeArg
 -keep class org.futo.circles.core.model.ShareUrlTypeArg
 -keep class org.futo.circles.model.PeopleCategoryTypeArg
 
diff --git a/app/src/main/java/org/futo/circles/feature/settings/SettingsDataSource.kt b/app/src/main/java/org/futo/circles/feature/settings/SettingsDataSource.kt
index c7e6b4c62839f74565392ca611443415c9c51bbc..b60abdd6c0994b32d729f35a07892e6917b64e5c 100644
--- a/app/src/main/java/org/futo/circles/feature/settings/SettingsDataSource.kt
+++ b/app/src/main/java/org/futo/circles/feature/settings/SettingsDataSource.kt
@@ -1,7 +1,7 @@
 package org.futo.circles.feature.settings
 
 import org.futo.circles.auth.feature.change_password.ChangePasswordDataSource
-import org.futo.circles.auth.feature.reauth.AuthConfirmationProvider
+import org.futo.circles.auth.feature.uia.flow.reauth.AuthConfirmationProvider
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.provider.MatrixSessionProvider
diff --git a/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt b/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt
index 11e4fa65c451fc76072c3bb3f490a6d8ad24d0c8..4af55af0b7303c9db62140652598328b99cf92f5 100644
--- a/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt
@@ -11,7 +11,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.MainActivity
 import org.futo.circles.R
-import org.futo.circles.auth.feature.reauth.ReAuthCancellationListener
+import org.futo.circles.auth.feature.uia.flow.reauth.ReAuthCancellationListener
 import org.futo.circles.auth.model.LogOut
 import org.futo.circles.auth.model.SwitchUser
 import org.futo.circles.core.base.CirclesAppConfig
diff --git a/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt b/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt
index 6c34556ccb2d072db69f66f11bef40307357f610..b855c5b75200670a795cbfdc99b6899b07be99b1 100644
--- a/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt
+++ b/app/src/main/java/org/futo/circles/feature/settings/SettingsNavigator.kt
@@ -1,10 +1,7 @@
 package org.futo.circles.feature.settings
 
 import androidx.navigation.fragment.findNavController
-import org.futo.circles.R
 import org.futo.circles.core.extensions.navigateSafe
-import org.futo.circles.core.extensions.showError
-import org.futo.circles.core.model.ShareUrlTypeArg
 
 class SettingsNavigator(private val fragment: SettingsFragment) {
 
@@ -25,7 +22,7 @@ class SettingsNavigator(private val fragment: SettingsFragment) {
 
     fun navigateToReAuthStages() {
         fragment.findNavController()
-            .navigateSafe(SettingsFragmentDirections.toReAuthStagesDialogFragment())
+            .navigateSafe(SettingsFragmentDirections.toUiaDialogFragment())
     }
 
 
diff --git a/app/src/main/java/org/futo/circles/feature/settings/SettingsViewModel.kt b/app/src/main/java/org/futo/circles/feature/settings/SettingsViewModel.kt
index bacd54ec9cd8d8ce4f1ac193703d0a9ac46950f3..b7d11b53094d02cdf78a36ab5c67d05e0bc35c49 100644
--- a/app/src/main/java/org/futo/circles/feature/settings/SettingsViewModel.kt
+++ b/app/src/main/java/org/futo/circles/feature/settings/SettingsViewModel.kt
@@ -2,13 +2,11 @@ package org.futo.circles.feature.settings
 
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
-import org.futo.circles.auth.feature.log_in.log_out.LogoutDataSource
 import org.futo.circles.auth.feature.token.RefreshTokenManager
 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.extensions.launchBg
-import org.futo.circles.core.feature.workspace.SharedCircleDataSource
 import org.futo.circles.core.provider.MatrixSessionProvider
 import org.matrix.android.sdk.internal.session.media.MediaUsageInfo
 import javax.inject.Inject
@@ -16,7 +14,6 @@ import javax.inject.Inject
 @HiltViewModel
 class SettingsViewModel @Inject constructor(
     private val settingsDataSource: SettingsDataSource,
-    private val logoutDataSource: LogoutDataSource,
     private val refreshTokenManager: RefreshTokenManager
 ) : ViewModel() {
 
@@ -31,7 +28,9 @@ class SettingsViewModel @Inject constructor(
     fun logOut() {
         launchBg {
             MatrixSessionProvider.currentSession?.let { refreshTokenManager.cancelTokenRefreshing(it) }
-            val result = logoutDataSource.logOut()
+            val result = createResult {
+                MatrixSessionProvider.getSessionOrThrow().signOutService().signOut(true)
+            }
             logOutLiveData.postValue(result)
         }
     }
diff --git a/app/src/main/res/navigation/settings_nav_graph.xml b/app/src/main/res/navigation/settings_nav_graph.xml
index dde2cbff7b7fbd4dbee57aaeac4942e4d20aa039..abe59b04798150055e186d0422d1561cf02f5b59 100644
--- a/app/src/main/res/navigation/settings_nav_graph.xml
+++ b/app/src/main/res/navigation/settings_nav_graph.xml
@@ -18,8 +18,8 @@
             android:id="@+id/to_activeSessionsDialogFragment"
             app:destination="@id/log_in_sessions_nav_graph" />
         <action
-            android:id="@+id/to_reAuthStagesDialogFragment"
-            app:destination="@id/reAuthStagesDialogFragment" />
+            android:id="@+id/to_uiaDialogFragment"
+            app:destination="@id/UIADialogFragment" />
         <action
             android:id="@+id/to_pushNotificationsSettingsDialogFragment"
             app:destination="@id/pushNotificationsSettingsDialogFragment" />
@@ -50,14 +50,13 @@
 
     <include app:graph="@navigation/log_in_sessions_nav_graph" />
 
-    <dialog
-        android:id="@+id/reAuthStagesDialogFragment"
-        android:name="org.futo.circles.auth.feature.reauth.ReAuthStagesDialogFragment"
-        tools:layout="@layout/fragment_login_stages" />
-
     <dialog
         android:id="@+id/manageSubscriptionDialogFragment"
         android:name="org.futo.circles.auth.feature.manage_subscription.ManageSubscriptionDialogFragment"
         tools:layout="@layout/dialog_fragment_manage_subscription" />
+    <dialog
+        android:id="@+id/UIADialogFragment"
+        android:name="org.futo.circles.auth.feature.uia.UIADialogFragment"
+        tools:layout="@layout/dialog_fragment_uia" />
 
 </navigation>
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/base/BaseAcceptTermsDataSource.kt b/auth/src/main/java/org/futo/circles/auth/base/BaseAcceptTermsDataSource.kt
deleted file mode 100644
index 950a3e2f67feaa7a574a766621718b304a87e5b7..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/base/BaseAcceptTermsDataSource.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.futo.circles.auth.base
-
-import androidx.lifecycle.MutableLiveData
-import org.futo.circles.auth.feature.log_in.stages.terms.LoginAcceptTermsDataSource
-import org.futo.circles.auth.feature.sign_up.terms.SignupAcceptTermsDataSource
-import org.futo.circles.auth.model.TermsListItem
-import org.futo.circles.auth.model.TermsModeArg
-import org.futo.circles.core.extensions.Response
-import javax.inject.Inject
-
-abstract class BaseAcceptTermsDataSource {
-
-    class Factory @Inject constructor(
-        private val loginStagesDataSourceFactory: BaseLoginStagesDataSource.Factory,
-        private val signupAcceptTermsDataSource: SignupAcceptTermsDataSource
-    ) {
-        fun create(mode: TermsModeArg): BaseAcceptTermsDataSource = when (mode) {
-            TermsModeArg.Login -> LoginAcceptTermsDataSource(
-                loginStagesDataSourceFactory.create(false)
-            )
-
-            TermsModeArg.Signup -> signupAcceptTermsDataSource
-            TermsModeArg.ReAuth -> LoginAcceptTermsDataSource(
-                loginStagesDataSourceFactory.create(true)
-            )
-        }
-    }
-
-    protected abstract fun getTermsList(): List<TermsListItem>
-    abstract suspend fun acceptTerms(): Response<Unit>
-
-    val termsListLiveData by lazy { MutableLiveData(getTermsList()) }
-
-    fun changeTermCheck(item: TermsListItem) {
-        termsListLiveData.value =
-            termsListLiveData.value?.map { if (it.id == item.id) it.copy(isChecked = !it.isChecked) else it }
-    }
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/base/BaseLoginStagesDataSource.kt b/auth/src/main/java/org/futo/circles/auth/base/BaseLoginStagesDataSource.kt
deleted file mode 100644
index 8fe2c916e7acd852570bd2072a0f8b7ce8e5ddd5..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/base/BaseLoginStagesDataSource.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-package org.futo.circles.auth.base
-
-import android.content.Context
-import androidx.lifecycle.MutableLiveData
-import org.futo.circles.auth.R
-import org.futo.circles.auth.feature.log_in.stages.LoginStagesDataSource
-import org.futo.circles.auth.feature.reauth.ReAuthStagesDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_BSSPEKE_OPRF_TYPE
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_BSSPEKE_SAVE_TYPE
-import org.futo.circles.core.base.SingleEventLiveData
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import org.matrix.android.sdk.api.auth.registration.Stage
-import org.matrix.android.sdk.api.util.JsonDict
-import javax.inject.Inject
-
-abstract class BaseLoginStagesDataSource(private val context: Context) {
-
-    class Factory @Inject constructor(
-        private val loginStagesDataSource: LoginStagesDataSource,
-        private val reAuthStagesDataSource: ReAuthStagesDataSource
-    ) {
-        fun create(isReAuth: Boolean): BaseLoginStagesDataSource = if (isReAuth)
-            reAuthStagesDataSource else loginStagesDataSource
-    }
-
-    val subtitleLiveData = MutableLiveData<String>()
-    val loginStageNavigationLiveData = SingleEventLiveData<LoginStageNavigationEvent>()
-
-    val stagesToComplete = mutableListOf<Stage>()
-    var currentStage: Stage? = null
-        private set
-
-    var userName: String = ""
-        private set
-    var domain: String = ""
-        private set
-
-    protected var userPassword: String = ""
-
-    fun startLoginStages(
-        loginStages: List<Stage>,
-        name: String,
-        serverDomain: String
-    ) {
-        userName = name
-        domain = serverDomain
-        userPassword = ""
-        currentStage = null
-        stagesToComplete.clear()
-        stagesToComplete.addAll(loginStages)
-        navigateToNextStage()
-    }
-
-    protected fun getIdentifier() = mapOf(
-        USER_PARAM_KEY to "@$userName:$domain",
-        TYPE_PARAM_KEY to LOGIN_PASSWORD_USER_ID_TYPE
-    )
-
-    abstract suspend fun performLoginStage(
-        authParams: JsonDict,
-        password: String? = null
-    ): Response<RegistrationResult>
-
-    private fun getCurrentStageIndex() =
-        stagesToComplete.indexOf(currentStage).takeIf { it != -1 } ?: 0
-
-    private fun setNextStage() {
-        currentStage = currentStage?.let {
-            stagesToComplete.getOrNull(getCurrentStageIndex() + 1)
-        } ?: stagesToComplete.firstOrNull()
-    }
-
-    protected fun isStageRetry(result: RegistrationResult?): Boolean {
-        val nextStageType =
-            ((result as? RegistrationResult.FlowResponse)?.flowResult?.missingStages?.lastOrNull() as? Stage.Other)?.type
-        return nextStageType == (currentStage as? Stage.Other)?.type && nextStageType != null
-    }
-
-    protected fun navigateToNextStage() {
-        setNextStage()
-        val event = when (val stage = currentStage) {
-            is Stage.Terms -> LoginStageNavigationEvent.Terms
-            is Stage.Other -> handleStageOther(stage.type)
-            else -> throw IllegalArgumentException(
-                context.getString(R.string.not_supported_stage_format, stage.toString())
-            )
-        }
-        event?.let { loginStageNavigationLiveData.postValue(it) }
-        updatePageSubtitle()
-    }
-
-    private fun handleStageOther(type: String): LoginStageNavigationEvent? = when (type) {
-        LOGIN_PASSWORD_TYPE -> LoginStageNavigationEvent.Password
-        DIRECT_LOGIN_PASSWORD_TYPE -> LoginStageNavigationEvent.DirectPassword
-        LOGIN_BSSPEKE_OPRF_TYPE -> LoginStageNavigationEvent.BSspekeLogin
-        LOGIN_BSSPEKE_VERIFY_TYPE -> null
-        REGISTRATION_BSSPEKE_OPRF_TYPE -> LoginStageNavigationEvent.BSspekeSignup
-        REGISTRATION_BSSPEKE_SAVE_TYPE -> null
-        else -> throw IllegalArgumentException(
-            context.getString(R.string.not_supported_stage_format, type)
-        )
-    }
-
-    private fun updatePageSubtitle() {
-        val size = stagesToComplete.size
-        val number = getCurrentStageIndex() + 1
-        val subtitle = context.getString(R.string.sign_up_stage_subtitle_format, number, size)
-        subtitleLiveData.postValue(subtitle)
-    }
-
-    companion object {
-        const val USER_PARAM_KEY = "user"
-
-        const val LOGIN_PASSWORD_TYPE = "m.login.password"
-        const val DIRECT_LOGIN_PASSWORD_TYPE = "m.login.password.direct"
-        const val LOGIN_BSSPEKE_OPRF_TYPE = "m.login.bsspeke-ecc.oprf"
-        const val LOGIN_BSSPEKE_VERIFY_TYPE = "m.login.bsspeke-ecc.verify"
-        const val TYPE_PARAM_KEY = "type"
-        const val LOGIN_PASSWORD_USER_ID_TYPE = "m.id.user"
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/base/LoginStageNavigationEvent.kt b/auth/src/main/java/org/futo/circles/auth/base/LoginStageNavigationEvent.kt
deleted file mode 100644
index 6824fab6b8bc40991dc0bddbcc433b6a438be29b..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/base/LoginStageNavigationEvent.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package org.futo.circles.auth.base
-
-enum class LoginStageNavigationEvent { Password, Terms, DirectPassword, BSspekeLogin, BSspekeSignup }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/base/PasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/base/PasswordDataSource.kt
deleted file mode 100644
index c4bcf8c20c3baf6d100d8fa35e7e3aa58cdfb751..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/base/PasswordDataSource.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.futo.circles.auth.base
-
-import org.futo.circles.core.extensions.Response
-
-interface PasswordDataSource {
-    suspend fun processPasswordStage(password: String): Response<Unit>
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/bsspeke/BSSpekeClientProvider.kt b/auth/src/main/java/org/futo/circles/auth/bsspeke/BSSpekeClientProvider.kt
index ccdf8710b1306131b524279f2bf27b80aaef0b0a..c43391f0d6658f81e0cbb26bc6385d0bddf75e5e 100644
--- a/auth/src/main/java/org/futo/circles/auth/bsspeke/BSSpekeClientProvider.kt
+++ b/auth/src/main/java/org/futo/circles/auth/bsspeke/BSSpekeClientProvider.kt
@@ -11,11 +11,6 @@ object BSSpekeClientProvider {
         clientInstance = BSSpekeClient("@$userPart:$domain", domain, password)
     }
 
-    fun initClient(userId: String, password: String) {
-        val serverId = userId.substringAfter(":")
-        clientInstance = BSSpekeClient(userId, serverId, password)
-    }
-
     fun clear() {
         clientInstance = null
     }
diff --git a/auth/src/main/java/org/futo/circles/auth/di/AuthModule.kt b/auth/src/main/java/org/futo/circles/auth/di/AuthModule.kt
deleted file mode 100644
index 455e6dc91ccde82d40f717fc8810da0fe7a3c969..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/di/AuthModule.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.futo.circles.auth.di
-
-import androidx.lifecycle.SavedStateHandle
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.InstallIn
-import dagger.hilt.android.components.ViewModelComponent
-import dagger.hilt.android.scopes.ViewModelScoped
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
-import org.futo.circles.auth.base.PasswordDataSource
-import org.futo.circles.auth.feature.log_in.stages.password.DirectLoginPasswordDataSource
-import org.futo.circles.auth.feature.log_in.stages.password.LoginBsSpekeDataSource
-import org.futo.circles.auth.feature.log_in.stages.password.LoginPasswordDataSource
-import org.futo.circles.auth.feature.sign_up.password.SignupBsSpekeDataSource
-import org.futo.circles.auth.feature.sign_up.password.SignupPasswordDataSource
-import org.futo.circles.auth.model.PasswordModeArg
-import org.futo.circles.core.extensions.getOrThrow
-
-@Module
-@InstallIn(ViewModelComponent::class)
-object AuthModule {
-
-    @Provides
-    @ViewModelScoped
-    fun providePasswordDataSource(
-        savedStateHandle: SavedStateHandle,
-        loginStagesDataSourceFactory: BaseLoginStagesDataSource.Factory,
-        loginBsSpekeStageDataSourceFactory: LoginBsSpekeDataSource.Factory,
-        directLoginPasswordDataSource: DirectLoginPasswordDataSource,
-        signupPasswordDataSource: SignupPasswordDataSource,
-        signupBsSpekeDataSource: SignupBsSpekeDataSource
-    ): PasswordDataSource = when (savedStateHandle.getOrThrow<PasswordModeArg>("mode")) {
-        PasswordModeArg.LoginPasswordStage -> LoginPasswordDataSource(
-            loginStagesDataSourceFactory.create(false)
-        )
-
-        PasswordModeArg.ReAuthPassword -> LoginPasswordDataSource(
-            loginStagesDataSourceFactory.create(true)
-        )
-
-        PasswordModeArg.LoginBsSpekeStage -> loginBsSpekeStageDataSourceFactory.create(
-            isReauth = false,
-            isChangePasswordEnroll = false
-        )
-
-        PasswordModeArg.ReAuthBsSpekeLogin -> loginBsSpekeStageDataSourceFactory.create(
-            isReauth = true,
-            isChangePasswordEnroll = false
-        )
-
-        PasswordModeArg.ReAuthBsSpekeSignup -> loginBsSpekeStageDataSourceFactory.create(
-            isReauth = true,
-            isChangePasswordEnroll = true
-        )
-
-        PasswordModeArg.LoginDirect -> directLoginPasswordDataSource
-        PasswordModeArg.SignupPasswordStage -> signupPasswordDataSource
-        PasswordModeArg.SignupBsSpekeStage -> signupBsSpekeDataSource
-    }
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDataSource.kt
index f2ab8ff523d7677734107364ac8b91b53995b9c9..74d62d6b1c5f08caa85608d4ade017639bf6edf6 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDataSource.kt
@@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
 import org.futo.circles.auth.R
-import org.futo.circles.auth.feature.reauth.AuthConfirmationProvider
+import org.futo.circles.auth.feature.uia.flow.reauth.AuthConfirmationProvider
 import org.futo.circles.auth.model.ActiveSession
 import org.futo.circles.auth.model.ActiveSessionListItem
 import org.futo.circles.auth.model.SessionHeader
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDialogFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDialogFragment.kt
index ffa3dd11c25bec100bb0ccb144f3446e137a713b..555bf16b8fe8464dd4abc0616a365ec421bd67fd 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDialogFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/active_sessions/ActiveSessionsDialogFragment.kt
@@ -79,7 +79,7 @@ class ActiveSessionsDialogFragment :
             error = { showError(getString(R.string.invalid_auth)) }
         )
         viewModel.startReAuthEventLiveData.observeData(this) {
-            findNavController().navigateSafe(ActiveSessionsDialogFragmentDirections.toReAuthStagesDialogFragment())
+            findNavController().navigateSafe(ActiveSessionsDialogFragmentDirections.toUiaDialogFragment())
         }
     }
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/change_password/ChangePasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/change_password/ChangePasswordDataSource.kt
index 54c5f06323ca014024e79152404125429b825cf6..1c1d71ca9d6645336531a8d5f2f957ffd7be0421 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/change_password/ChangePasswordDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/change_password/ChangePasswordDataSource.kt
@@ -3,7 +3,7 @@ package org.futo.circles.auth.feature.change_password
 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.reauth.AuthConfirmationProvider
+import org.futo.circles.auth.feature.uia.flow.reauth.AuthConfirmationProvider
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.provider.MatrixSessionProvider
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 de612add29a36b41e068288aa7b4dc946aca1d86..b0be875dfcee95d12dd05cb03b6eabfea703d734 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
@@ -15,6 +15,7 @@ import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentLogInBinding
 import org.futo.circles.auth.feature.log_in.switch_user.list.SwitchUsersAdapter
 import org.futo.circles.auth.feature.log_in.switch_user.list.SwitchUsersViewHolder
+import org.futo.circles.auth.model.ForgotPassword
 import org.futo.circles.auth.model.RemoveUser
 import org.futo.circles.core.base.CirclesAppConfig
 import org.futo.circles.core.base.NetworkObserver
@@ -98,7 +99,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))
@@ -118,18 +119,23 @@ class LogInFragment : Fragment(R.layout.fragment_log_in), HasLoadingState {
             btnSignUp.setOnClickListener {
                 findNavController().navigateSafe(LogInFragmentDirections.toSignUpFragment())
             }
-            btnLogin.setOnClickListener {
-                val userName = binding.tilUserName.getText()
-                if (userName.isEmpty()) {
-                    showError(getString(R.string.username_can_not_be_empty))
-                    return@setOnClickListener
-                }
-                startLoading(btnLogin)
-                viewModel.startLogInFlow(userName, getDomain())
+            btnLogin.setOnClickListener { startLogin(false) }
+            btnForgotPassword.setOnClickListener {
+                withConfirmation(ForgotPassword()) { startLogin(true) }
             }
         }
     }
 
+    private fun startLogin(isForgotPassword: Boolean) {
+        val userName = binding.tilUserName.getText()
+        if (userName.isEmpty()) {
+            showError(getString(R.string.username_can_not_be_empty))
+            return
+        }
+        startLoading(binding.btnLogin)
+        viewModel.startLogInFlow(userName, getDomain(), isForgotPassword)
+    }
+
     private fun getDomain() = binding.tvDomain.text.toString().takeIf { it.isNotEmpty() }
         ?: CirclesAppConfig.serverDomains.first()
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInViewModel.kt
index 9c50802a2f9e2a727eadc3ab686d9dd0b91a9371..006376514579c6b6155eccb07c8b6d68bd1d57cb 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInViewModel.kt
@@ -21,10 +21,10 @@ class LogInViewModel @Inject constructor(
     val switchUsersLiveData = MutableLiveData(switchUserDataSource.getSwitchUsersList())
     val navigateToBottomMenuScreenLiveData = SingleEventLiveData<Unit>()
 
-    fun startLogInFlow(userName: String, domain: String) {
+    fun startLogInFlow(userName: String, domain: String, isForgotPassword: Boolean) {
         switchUserDataSource.getSessionCredentialsIdByUserInfo(userName, domain)
             ?.let { resumeSwitchUserSession(it) }
-            ?: login(userName, domain)
+            ?: login(userName, domain, isForgotPassword)
     }
 
     fun removeSwitchUser(id: String) {
@@ -43,9 +43,9 @@ class LogInViewModel @Inject constructor(
         }
     }
 
-    private fun login(userName: String, domain: String) {
+    private fun login(userName: String, domain: String, isForgotPassword: Boolean) {
         launchBg {
-            val loginResult = loginDataSource.startLogin(userName, domain)
+            val loginResult = loginDataSource.startLogin(userName, domain, isForgotPassword)
             loginResultLiveData.postValue(loginResult)
         }
     }
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 decc0120ddd640a542156b46c10cd5c6ce953282..6e3a96ef5480e4e5f60b4fa7350841ab56884b7c 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
@@ -3,12 +3,18 @@ package org.futo.circles.auth.feature.log_in
 import android.content.Context
 import dagger.hilt.android.qualifiers.ApplicationContext
 import org.futo.circles.auth.R
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.DIRECT_LOGIN_PASSWORD_TYPE
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.LOGIN_PASSWORD_TYPE
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.LOGIN_PASSWORD_USER_ID_TYPE
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.USER_PARAM_KEY
-import org.futo.circles.auth.feature.log_in.stages.LoginStagesDataSource
+import org.futo.circles.auth.feature.uia.UIADataSource
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.DIRECT_LOGIN_PASSWORD_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_BSSPEKE_SAVE_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_EMAIL_SUBMIT_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_BSSPEKE_VERIFY_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_EMAIL_SUBMIT_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_PASSWORD_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_PASSWORD_USER_ID_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.USER_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.auth.model.UIAFlowType
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.provider.MatrixInstanceProvider
 import org.futo.circles.core.utils.HomeServerUtils.buildHomeServerConfigFromDomain
@@ -17,42 +23,76 @@ import javax.inject.Inject
 
 class LoginDataSource @Inject constructor(
     @ApplicationContext private val context: Context,
-    private val loginStagesDataSource: LoginStagesDataSource
+    private val uiaFactory: UIADataSource.Factory
 ) {
 
+
     private val authService by lazy { MatrixInstanceProvider.matrix.authenticationService() }
 
     suspend fun startLogin(
         userName: String,
-        domain: String
+        domain: String,
+        isForgotPassword: Boolean
     ) = createResult {
         authService.cancelPendingLoginOrRegistration()
-        val stages = prepareLoginStages(userName, domain)
-        loginStagesDataSource.startLoginStages(stages, userName, domain)
+        val stages = prepareLoginStages(userName, domain, isForgotPassword)
+        val uiaDataSource = UIADataSourceProvider.create(
+            if (isForgotPassword) UIAFlowType.ForgotPassword else UIAFlowType.Login,
+            uiaFactory
+        )
+        uiaDataSource.startUIAStages(stages, domain, userName)
     }
 
     private suspend fun prepareLoginStages(
         userName: String,
-        domain: String
+        domain: String,
+        isForgotPassword: Boolean
     ): List<Stage> {
         val homeServerConfig = buildHomeServerConfigFromDomain(domain)
         val supportedLoginMethods = authService.getLoginFlow(homeServerConfig).supportedLoginTypes
 
-        return if (isPasswordLogin(supportedLoginMethods))
-            listOf(Stage.Other(true, DIRECT_LOGIN_PASSWORD_TYPE, null))
-        else getCircleLoginStages(userName, domain)
+        return if (isPasswordLogin(supportedLoginMethods)) {
+            if (isForgotPassword) throw IllegalArgumentException("Forgot password is only available for Circles domains")
+            else listOf(Stage.Other(true, DIRECT_LOGIN_PASSWORD_TYPE, null))
+        } else getCircleStages(userName, domain, isForgotPassword)
     }
 
     private fun isPasswordLogin(methods: List<String>) = methods.contains(LOGIN_PASSWORD_TYPE)
 
-    private suspend fun getCircleLoginStages(userName: String, domain: String): List<Stage> {
+    private suspend fun getCircleStages(
+        userName: String,
+        domain: String,
+        isForgotPassword: Boolean
+    ): List<Stage> {
         val identifierParams = mapOf(
             USER_PARAM_KEY to "@$userName:$domain",
             TYPE_PARAM_KEY to LOGIN_PASSWORD_USER_ID_TYPE
         )
         val flows = authService.getLoginWizard()
             .getAllLoginFlows(identifierParams, context.getString(R.string.initial_device_name))
-        return flows.firstOrNull()
+
+        val stages = if (isForgotPassword) getCircleStagesForForgotPassword(flows)
+        else getCircleStagesForLogin(flows)
+
+        return stages
             ?: throw IllegalArgumentException(context.getString(R.string.unsupported_login_method))
     }
+
+    private fun getCircleStagesForLogin(flows: List<List<Stage>>): List<Stage>? =
+        flows.firstOrNull { stages ->
+            stages.firstOrNull { stage ->
+                (stage as? Stage.Other)?.type == LOGIN_BSSPEKE_VERIFY_TYPE
+            } != null
+        }
+
+    private fun getCircleStagesForForgotPassword(flows: List<List<Stage>>): List<Stage>? =
+        flows.firstOrNull { stages ->
+            val containsEmailStage = stages.firstOrNull { stage ->
+                (stage as? Stage.Other)?.type == LOGIN_EMAIL_SUBMIT_TOKEN_TYPE
+            } != null
+            val containsSetPassword = stages.firstOrNull { stage ->
+                (stage as? Stage.Other)?.type == ENROLL_BSSPEKE_SAVE_TYPE
+            } != null
+            containsEmailStage && containsSetPassword
+        }
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/log_out/LogoutDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/log_out/LogoutDataSource.kt
deleted file mode 100644
index 95ce658c1636f8e22e635f765ca84d1b08645504..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/log_out/LogoutDataSource.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.futo.circles.auth.feature.log_in.log_out
-
-import org.futo.circles.core.extensions.createResult
-import org.futo.circles.core.provider.MatrixSessionProvider
-import javax.inject.Inject
-
-class LogoutDataSource @Inject constructor() {
-
-    suspend fun logOut() = createResult {
-        MatrixSessionProvider.getSessionOrThrow().signOutService().signOut(true)
-    }
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt
deleted file mode 100644
index 610ea348f09646a916b739aa2de0805cef7b74e8..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages
-
-import android.net.Uri
-import android.os.Bundle
-import android.view.View
-import androidx.activity.OnBackPressedCallback
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.NavHostFragment
-import androidx.navigation.fragment.findNavController
-import by.kirich1409.viewbindingdelegate.viewBinding
-import dagger.hilt.android.AndroidEntryPoint
-import org.futo.circles.auth.R
-import org.futo.circles.auth.base.LoginStageNavigationEvent
-import org.futo.circles.auth.databinding.FragmentLoginStagesBinding
-import org.futo.circles.auth.feature.log_in.recovery.EnterPassPhraseDialog
-import org.futo.circles.auth.feature.log_in.recovery.EnterPassPhraseDialogListener
-import org.futo.circles.core.base.NetworkObserver
-import org.futo.circles.core.extensions.navigateSafe
-import org.futo.circles.core.extensions.observeData
-import org.futo.circles.core.extensions.observeResponse
-import org.futo.circles.core.extensions.onBackPressed
-import org.futo.circles.core.extensions.setEnabledViews
-import org.futo.circles.core.extensions.showDialog
-import org.futo.circles.core.extensions.showError
-import org.futo.circles.core.base.fragment.BackPressOwner
-import org.futo.circles.core.view.LoadingDialog
-
-@AndroidEntryPoint
-class LogInStagesFragment : Fragment(R.layout.fragment_login_stages),
-    BackPressOwner {
-
-    private val viewModel by viewModels<LoginStagesViewModel>()
-    private val binding by viewBinding(FragmentLoginStagesBinding::bind)
-
-    private val loadingDialog by lazy { LoadingDialog(requireContext()) }
-    private var enterPassPhraseDialog: EnterPassPhraseDialog? = null
-
-    private val deviceIntentLauncher = registerForActivityResult(
-        ActivityResultContracts.GetContent()
-    ) { uri ->
-        uri ?: return@registerForActivityResult
-        enterPassPhraseDialog?.selectFile(uri)
-    }
-
-    private val childNavHostFragment by lazy {
-        childFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
-    }
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-        binding.toolbar.setNavigationOnClickListener { onBackPressed() }
-        binding.toolbar.title = getString(R.string.log_in)
-        setupObservers()
-    }
-
-    private fun setupObservers() {
-        NetworkObserver.observe(this) { setEnabledViews(it) }
-        viewModel.loginStageNavigationLiveData.observeData(this) { event ->
-            val id = when (event) {
-                LoginStageNavigationEvent.DirectPassword -> R.id.to_direct_login
-                LoginStageNavigationEvent.Password -> R.id.to_password
-                LoginStageNavigationEvent.Terms -> R.id.to_acceptTerms
-                LoginStageNavigationEvent.BSspekeLogin -> R.id.to_bsspeke
-                else -> throw IllegalArgumentException(getString(R.string.not_supported_navigation_event))
-            }
-            binding.navHostFragment.findNavController().navigateSafe(id)
-        }
-        viewModel.subtitleLiveData.observeData(this) {
-            binding.toolbar.subtitle = it
-        }
-        viewModel.restoreKeysLiveData.observeResponse(
-            this,
-            error = {
-                showError(it)
-                loadingDialog.dismiss()
-            }
-        )
-        viewModel.passPhraseLoadingLiveData.observeData(this) {
-            loadingDialog.handleLoading(it)
-        }
-        viewModel.loginNavigationLiveData.observeData(this) { event ->
-            when (event) {
-                LoginNavigationEvent.Main -> navigateToHome()
-                LoginNavigationEvent.PassPhrase -> showPassPhraseDialog()
-                else -> navigateToHome()
-            }
-        }
-        viewModel.messageEventLiveData.observeData(this) { messageId ->
-            showError(requireContext().getString(messageId))
-        }
-    }
-
-    private fun showPassPhraseDialog() {
-        enterPassPhraseDialog =
-            EnterPassPhraseDialog(requireContext(), object : EnterPassPhraseDialogListener {
-                override fun onRestoreBackupWithPassphrase(passphrase: String) {
-                    viewModel.restoreBackupWithPassPhrase(passphrase)
-                }
-
-                override fun onRestoreBackupWithRawKey(key: String) {
-                    viewModel.restoreBackupWithRawKey(key)
-                }
-
-                override fun onRestoreBackup(uri: Uri) {
-                    viewModel.restoreBackup(uri)
-                }
-
-                override fun onDoNotRestore() {
-                    viewModel.onDoNotRestoreBackup()
-                }
-
-                override fun onSelectFileClicked() {
-                    deviceIntentLauncher.launch(recoveryKeyMimeType)
-                }
-            }).apply {
-                setOnDismissListener { enterPassPhraseDialog = null }
-                show()
-            }
-    }
-
-    private fun navigateToHome() {
-        findNavController().navigateSafe(LogInStagesFragmentDirections.toHomeFragment())
-    }
-
-    private fun showDiscardDialog() {
-        showDialog(
-            titleResIdRes = R.string.discard_current_login_progress,
-            negativeButtonVisible = true,
-            positiveAction = { findNavController().popBackStack() })
-    }
-
-    override fun onChildBackPress(callback: OnBackPressedCallback) {
-        val includedFragmentsManager = childNavHostFragment.childFragmentManager
-        if (includedFragmentsManager.backStackEntryCount == 0) {
-            callback.remove()
-            onBackPressed()
-        } else {
-            showDiscardDialog()
-        }
-    }
-
-    companion object {
-        private const val recoveryKeyMimeType = "text/plain"
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LoginStagesDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LoginStagesDataSource.kt
deleted file mode 100644
index c163b972f8c28f80bc11ce80015db0e1c5ffe464..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LoginStagesDataSource.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages
-
-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.base.BaseLoginStagesDataSource
-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.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.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
-) : BaseLoginStagesDataSource(context) {
-
-    val loginNavigationLiveData = SingleEventLiveData<LoginNavigationEvent>()
-    val passPhraseLoadingLiveData = restoreBackupDataSource.loadingLiveData
-    val messageEventLiveData = SingleEventLiveData<Int>()
-
-    override suspend fun performLoginStage(
-        authParams: JsonDict,
-        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 { stageCompleted(result.data, password) }
-        return result
-    }
-
-    suspend fun stageCompleted(result: RegistrationResult, password: String?) {
-        if (isStageRetry(result)) return
-        password?.let { userPassword = it }
-        (result as? RegistrationResult.Success)?.let {
-            finishLogin(it.session)
-        } ?: navigateToNextStage()
-    }
-
-    private suspend fun finishLogin(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 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/feature/log_in/stages/LoginStagesViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LoginStagesViewModel.kt
deleted file mode 100644
index c8790b38ba33ffb4d8625ebe3a7bf704b4cdf855..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LoginStagesViewModel.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages
-
-import android.net.Uri
-import androidx.lifecycle.ViewModel
-import dagger.hilt.android.lifecycle.HiltViewModel
-import org.futo.circles.core.base.SingleEventLiveData
-import org.futo.circles.core.extensions.Response
-import org.futo.circles.core.extensions.launchBg
-import javax.inject.Inject
-
-@HiltViewModel
-class LoginStagesViewModel @Inject constructor(
-    private val loginStagesDataSource: LoginStagesDataSource
-) : ViewModel() {
-
-    val subtitleLiveData = loginStagesDataSource.subtitleLiveData
-    val loginStageNavigationLiveData = loginStagesDataSource.loginStageNavigationLiveData
-    val restoreKeysLiveData = SingleEventLiveData<Response<Unit>>()
-    val loginNavigationLiveData = loginStagesDataSource.loginNavigationLiveData
-    val passPhraseLoadingLiveData = loginStagesDataSource.passPhraseLoadingLiveData
-    val messageEventLiveData = loginStagesDataSource.messageEventLiveData
-
-    fun restoreBackupWithPassPhrase(passphrase: String) {
-        launchBg {
-            val result = loginStagesDataSource.restoreBackupWithPassphrase(passphrase)
-            restoreKeysLiveData.postValue(result)
-        }
-    }
-
-    fun restoreBackupWithRawKey(rawKey: String) {
-        launchBg {
-            val result = loginStagesDataSource.restoreBackupWithRawKey(rawKey)
-            restoreKeysLiveData.postValue(result)
-        }
-    }
-
-    fun restoreBackup(uri: Uri) {
-        launchBg {
-            restoreKeysLiveData.postValue(loginStagesDataSource.restoreBackup(uri))
-        }
-    }
-
-    fun onDoNotRestoreBackup() {
-        loginStagesDataSource.navigateToMain()
-    }
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/DirectLoginPasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/DirectLoginPasswordDataSource.kt
deleted file mode 100644
index 84d76d1427de0ccd36c8f93c23ec3479755da647..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/DirectLoginPasswordDataSource.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages.password
-
-import android.content.Context
-import dagger.hilt.android.qualifiers.ApplicationContext
-import org.futo.circles.auth.R
-import org.futo.circles.auth.base.PasswordDataSource
-import org.futo.circles.core.extensions.Response
-import org.futo.circles.core.extensions.createResult
-import org.futo.circles.auth.feature.log_in.stages.LoginStagesDataSource
-import org.futo.circles.core.provider.MatrixInstanceProvider
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import javax.inject.Inject
-
-class DirectLoginPasswordDataSource @Inject constructor(
-    @ApplicationContext private val context: Context,
-    private val loginStagesDataSource: LoginStagesDataSource
-) : PasswordDataSource {
-
-    override suspend fun processPasswordStage(password: String): Response<Unit> {
-        val result = createResult {
-            MatrixInstanceProvider.matrix.authenticationService().getLoginWizard().login(
-                login = loginStagesDataSource.userName,
-                password = password,
-                initialDeviceName = context.getString(R.string.initial_device_name)
-            )
-        }
-        return when (result) {
-            is Response.Success -> {
-                loginStagesDataSource.stageCompleted(
-                    RegistrationResult.Success(result.data), password
-                )
-                Response.Success(Unit)
-            }
-
-            is Response.Error -> result
-        }
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/LoginBsSpekeDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/LoginBsSpekeDataSource.kt
deleted file mode 100644
index 3977d00b3523960ddedc8053145d477dbfd4d6a9..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/LoginBsSpekeDataSource.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages.password
-
-import android.content.Context
-import dagger.hilt.android.qualifiers.ApplicationContext
-import org.futo.circles.auth.base.BaseBsSpekeStageDataSource
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import org.matrix.android.sdk.api.auth.registration.Stage
-import org.matrix.android.sdk.api.util.JsonDict
-import javax.inject.Inject
-
-class LoginBsSpekeDataSource(
-    context: Context,
-    private val isChangePasswordEnroll: Boolean,
-    private val loginStagesDataSource: BaseLoginStagesDataSource
-) : BaseBsSpekeStageDataSource(context) {
-
-    class Factory @Inject constructor(
-        @ApplicationContext private val context: Context,
-        private val loginStagesDataSourceFactory: BaseLoginStagesDataSource.Factory
-    ) {
-        fun create(isReauth: Boolean, isChangePasswordEnroll: Boolean): LoginBsSpekeDataSource =
-            LoginBsSpekeDataSource(
-                context,
-                isChangePasswordEnroll,
-                loginStagesDataSourceFactory.create(isReauth)
-            )
-    }
-
-    override val userName: String get() = loginStagesDataSource.userName
-    override val domain: String get() = loginStagesDataSource.domain
-    override val isLoginMode: Boolean get() = !isChangePasswordEnroll
-    override fun getStages(): List<Stage> = loginStagesDataSource.stagesToComplete
-
-    override suspend fun performAuthStage(
-        authParams: JsonDict,
-        password: String?
-    ): Response<RegistrationResult> = loginStagesDataSource.performLoginStage(authParams, password)
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/LoginPasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/LoginPasswordDataSource.kt
deleted file mode 100644
index 6b194816fe362622374dc87e448a26a4ef130673..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/password/LoginPasswordDataSource.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages.password
-
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.LOGIN_PASSWORD_TYPE
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.base.PasswordDataSource
-import org.futo.circles.core.extensions.Response
-
-class LoginPasswordDataSource(
-    private val loginStagesDataSource: BaseLoginStagesDataSource
-) : PasswordDataSource {
-
-    override suspend fun processPasswordStage(password: String): Response<Unit> {
-        val result = loginStagesDataSource.performLoginStage(
-            mapOf(
-                TYPE_PARAM_KEY to LOGIN_PASSWORD_TYPE,
-                PASSWORD_PARAM_KEY to password
-            ), password
-        )
-        return when (result) {
-            is Response.Success -> Response.Success(Unit)
-            is Response.Error -> result
-        }
-    }
-
-    companion object {
-        const val PASSWORD_PARAM_KEY = "password"
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/terms/LoginAcceptTermsDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/terms/LoginAcceptTermsDataSource.kt
deleted file mode 100644
index f3949b9fc441de476ca67a89b95531e8f6706fe6..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/terms/LoginAcceptTermsDataSource.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.futo.circles.auth.feature.log_in.stages.terms
-
-import org.futo.circles.auth.base.BaseAcceptTermsDataSource
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.extensions.toTermsListItems
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
-import org.matrix.android.sdk.api.auth.registration.Stage
-import javax.inject.Inject
-
-class LoginAcceptTermsDataSource @Inject constructor(
-    private val loginStagesDataSource: BaseLoginStagesDataSource
-) : BaseAcceptTermsDataSource() {
-
-    override suspend fun acceptTerms(): Response<Unit> {
-        val result = loginStagesDataSource.performLoginStage(
-            mapOf(TYPE_PARAM_KEY to LoginFlowTypes.TERMS)
-        )
-        return when (result) {
-            is Response.Success -> Response.Success(Unit)
-            is Response.Error -> result
-        }
-    }
-
-    override fun getTermsList() =
-        (loginStagesDataSource.currentStage as? Stage.Terms)?.policies?.toTermsListItems()
-            ?: emptyList()
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/recovery/EnterPassPhraseDialog.kt b/auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialog.kt
similarity index 98%
rename from auth/src/main/java/org/futo/circles/auth/feature/log_in/recovery/EnterPassPhraseDialog.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialog.kt
index 25f0035fe5be9bd6fd5cb3bd76fc11cd0d443acd..de00621cff4dc6f8493242b9408d763d5efddc75 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/recovery/EnterPassPhraseDialog.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialog.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.log_in.recovery
+package org.futo.circles.auth.feature.pass_phrase.recovery
 
 import android.app.ActionBar
 import android.content.Context
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/recovery/EnterPassPhraseDialogListener.kt b/auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialogListener.kt
similarity index 81%
rename from auth/src/main/java/org/futo/circles/auth/feature/log_in/recovery/EnterPassPhraseDialogListener.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialogListener.kt
index 9c750f9129879994768ece2c415816522a3d4f13..f8407df944e16138403cfcbcd874f396df64a22b 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/recovery/EnterPassPhraseDialogListener.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/pass_phrase/recovery/EnterPassPhraseDialogListener.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.log_in.recovery
+package org.futo.circles.auth.feature.pass_phrase.recovery
 
 import android.net.Uri
 
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStageViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStageViewModel.kt
deleted file mode 100644
index 5761913b75eccb53ee233439f6080d600a8e723d..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStageViewModel.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.futo.circles.auth.feature.reauth
-
-import androidx.lifecycle.ViewModel
-import dagger.hilt.android.lifecycle.HiltViewModel
-import javax.inject.Inject
-
-@HiltViewModel
-class ReAuthStageViewModel @Inject constructor(reAuthStagesDataSource: ReAuthStagesDataSource) : ViewModel() {
-
-    val subtitleLiveData = reAuthStagesDataSource.subtitleLiveData
-    val loginStageNavigationLiveData = reAuthStagesDataSource.loginStageNavigationLiveData
-    val finishReAuthEventLiveData = reAuthStagesDataSource.finishReAuthEventLiveData
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStagesDialogFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStagesDialogFragment.kt
deleted file mode 100644
index 2c6508f37530dd9b88aadbd468d8e2c84e7b2384..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStagesDialogFragment.kt
+++ /dev/null
@@ -1,106 +0,0 @@
-package org.futo.circles.auth.feature.reauth
-
-import android.app.Dialog
-import android.os.Bundle
-import android.view.View
-import androidx.activity.OnBackPressedCallback
-import androidx.fragment.app.viewModels
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.NavHostFragment
-import androidx.navigation.fragment.findNavController
-import dagger.hilt.android.AndroidEntryPoint
-import org.futo.circles.auth.R
-import org.futo.circles.auth.base.LoginStageNavigationEvent
-import org.futo.circles.auth.databinding.FragmentLoginStagesBinding
-import org.futo.circles.core.base.fragment.BackPressOwner
-import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment
-import org.futo.circles.core.extensions.navigateSafe
-import org.futo.circles.core.extensions.observeData
-import org.futo.circles.core.extensions.onBackPressed
-import org.futo.circles.core.extensions.showDialog
-
-@AndroidEntryPoint
-class ReAuthStagesDialogFragment :
-    BaseFullscreenDialogFragment(FragmentLoginStagesBinding::inflate), BackPressOwner {
-
-    private val viewModel by viewModels<ReAuthStageViewModel>()
-
-    private val binding by lazy {
-        getBinding() as FragmentLoginStagesBinding
-    }
-
-    private val childNavHostFragment by lazy {
-        childFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
-    }
-
-    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
-        object : Dialog(requireContext(), theme) {
-            @Suppress("OVERRIDE_DEPRECATION")
-            override fun onBackPressed() {
-                activity?.onBackPressedDispatcher?.onBackPressed()
-            }
-        }
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-        binding.toolbar.apply {
-            title = getString(R.string.confirm_auth)
-            setNavigationOnClickListener { handleBackAction() }
-        }
-        setupObservers()
-    }
-
-    private fun setupObservers() {
-        viewModel.loginStageNavigationLiveData.observeData(this) { event ->
-            val id = when (event) {
-                LoginStageNavigationEvent.DirectPassword -> R.id.to_direct_login
-                LoginStageNavigationEvent.Password -> R.id.to_reAuthPassword
-                LoginStageNavigationEvent.Terms -> R.id.to_ReAuthAcceptTerms
-                LoginStageNavigationEvent.BSspekeLogin -> R.id.to_reAuthBsSpekeLogin
-                LoginStageNavigationEvent.BSspekeSignup -> R.id.to_reAuthBsSpekeSignup
-                else -> throw IllegalArgumentException(getString(R.string.not_supported_navigation_event))
-            }
-            binding.navHostFragment.findNavController().navigateSafe(id)
-        }
-        viewModel.subtitleLiveData.observeData(this) {
-            binding.toolbar.subtitle = it
-        }
-        viewModel.finishReAuthEventLiveData.observeData(this) {
-            dismiss()
-        }
-    }
-
-    private fun showDiscardDialog() {
-        showDialog(
-            titleResIdRes = R.string.discard_current_login_progress,
-            negativeButtonVisible = true,
-            positiveAction = {
-                cancelReAuth()
-                findNavController().popBackStack()
-            }
-        )
-    }
-
-    override fun onChildBackPress(callback: OnBackPressedCallback) {
-        handleBackAction(callback)
-    }
-
-    private fun handleBackAction(callback: OnBackPressedCallback? = null) {
-        val includedFragmentsManager = childNavHostFragment.childFragmentManager
-        if (includedFragmentsManager.backStackEntryCount == 1) {
-            cancelReAuth()
-            callback?.remove()
-            onBackPressed()
-        } else {
-            showDiscardDialog()
-        }
-    }
-
-    private fun cancelReAuth() {
-        parentFragment?.childFragmentManager?.fragments?.forEach {
-            (it as? ReAuthCancellationListener)?.onReAuthCanceled()
-        }
-
-    }
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpDataSource.kt
index b7319fdbbd1f5719ebaeeb5d9a0fc549ce0ae33c..a123c8f8d91c845efd14c62cf40e2e539142b0f5 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpDataSource.kt
@@ -1,162 +1,66 @@
 package org.futo.circles.auth.feature.sign_up
 
 import android.content.Context
-import androidx.lifecycle.MutableLiveData
 import dagger.hilt.android.qualifiers.ApplicationContext
 import org.futo.circles.auth.R
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
-import org.futo.circles.auth.bsspeke.BSSpekeClientProvider
-import org.futo.circles.auth.feature.pass_phrase.create.CreatePassPhraseDataSource
-import org.futo.circles.core.base.SingleEventLiveData
-import org.futo.circles.core.extensions.Response
+import org.futo.circles.auth.feature.uia.UIADataSource
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.SUBSCRIPTION_FREE_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.SUBSCRPTION_GOOGLE_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.auth.model.UIAFlowType
+import org.futo.circles.core.base.CirclesAppConfig
 import org.futo.circles.core.extensions.createResult
 import org.futo.circles.core.provider.MatrixInstanceProvider
-import org.futo.circles.core.provider.MatrixSessionProvider
-import org.futo.circles.core.provider.PreferencesProvider
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import org.futo.circles.core.utils.HomeServerUtils.buildHomeServerConfigFromDomain
 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 SignUpNavigationEvents { TokenValidation, Subscription, AcceptTerm, ValidateEmail, Password, BSspeke, Username }
-
-@Singleton
 class SignUpDataSource @Inject constructor(
     @ApplicationContext private val context: Context,
-    private val createPassPhraseDataSource: CreatePassPhraseDataSource,
-    private val preferencesProvider: PreferencesProvider
+    private val uiaFactory: UIADataSource.Factory
 ) {
 
-    val subtitleLiveData = MutableLiveData<String>()
-    val navigationLiveData = SingleEventLiveData<SignUpNavigationEvents>()
-    val finishRegistrationLiveData = SingleEventLiveData<Response<Unit>>()
-    val passPhraseLoadingLiveData = createPassPhraseDataSource.loadingLiveData
-
-    val stagesToComplete = mutableListOf<Stage>()
-
-    var currentStage: Stage? = null
-        private set
-
-    var userName: String = ""
-        private set
-    var domain: String = ""
-        private set
-
-    suspend fun startSignUpStages(
-        stages: List<Stage>,
-        serverDomain: String
-    ) {
-        currentStage = null
-        stagesToComplete.clear()
-        domain = serverDomain
-        stagesToComplete.addAll(stages)
-        navigateToNextStage()
-    }
+    private var registrationFlowsForDomain: Pair<String, List<List<Stage>>>? = null
 
-    suspend fun performRegistrationStage(
-        authParams: JsonDict,
-        name: String? = null
-    ): Response<RegistrationResult> {
-        val wizard = MatrixInstanceProvider.matrix.authenticationService().getRegistrationWizard()
-        val result = createResult {
-            wizard.registrationCustom(
-                authParams,
-                context.getString(R.string.initial_device_name),
-                true
-            )
+    suspend fun getAuthFlowsFor(domain: String) = createResult {
+        registrationFlowsForDomain = null
+        val authService = MatrixInstanceProvider.matrix.authenticationService().apply {
+            cancelPendingLoginOrRegistration()
+            initiateAuth(buildHomeServerConfigFromDomain(domain))
         }
-
-        (result as? Response.Success)?.let {
-            name?.let { userName = it }
-            stageCompleted(result.data)
+        authService.getRegistrationWizard().getAllRegistrationFlows().also {
+            registrationFlowsForDomain = domain to it
         }
-        return result
-    }
-
-    private suspend fun stageCompleted(result: RegistrationResult?) {
-        if (isStageRetry(result)) return
-        (result as? RegistrationResult.Success)?.let {
-            finishRegistrationLiveData.postValue(finishRegistration(it.session))
-        } ?: navigateToNextStage()
     }
 
-    fun clearSubtitle() {
-        subtitleLiveData.postValue("")
-    }
-
-    private fun isStageRetry(result: RegistrationResult?): Boolean {
-        val nextStageType =
-            ((result as? RegistrationResult.FlowResponse)?.flowResult?.missingStages?.firstOrNull() as? Stage.Other)?.type
-        return nextStageType == (currentStage as? Stage.Other)?.type && nextStageType != null
-    }
-
-    private suspend fun finishRegistration(session: Session) = createResult {
-        MatrixInstanceProvider.matrix.authenticationService().reset()
-        MatrixSessionProvider.awaitForSessionStart(session)
-        preferencesProvider.setShouldShowAllExplanations()
-        createPassPhraseDataSource.createPassPhraseBackup()
-        BSSpekeClientProvider.clear()
-    }
+    suspend fun startNewRegistration(isSubscription: Boolean) = createResult {
+        val (domain, flows) = registrationFlowsForDomain ?: throw IllegalArgumentException(
+            context.getString(R.string.wrong_signup_config)
+        )
+        val stages = if (isSubscription) getSubscriptionSignupStages(flows)
+        else getFreeSignupStages(flows)
 
-    private fun getCurrentStageIndex() =
-        stagesToComplete.indexOf(currentStage).takeIf { it != -1 } ?: 0
+        stages ?: throw IllegalArgumentException(context.getString(R.string.wrong_signup_config))
 
-    private fun setNextStage() {
-        currentStage = currentStage?.let {
-            stagesToComplete.getOrNull(getCurrentStageIndex() + 1)
-        } ?: stagesToComplete.firstOrNull()
+        val uiaDataSource = UIADataSourceProvider.create(UIAFlowType.Signup, uiaFactory)
+        uiaDataSource.startUIAStages(stages, domain)
     }
 
-    private suspend fun navigateToNextStage() {
-        setNextStage()
-        val event = when (val stage = currentStage) {
-            is Stage.Terms -> SignUpNavigationEvents.AcceptTerm
-            is Stage.Other -> handleStageOther(stage.type)
-            else -> throw IllegalArgumentException(
-                context.getString(R.string.not_supported_stage_format, stage.toString())
-            )
-        }
-        event?.let { navigationLiveData.postValue(it) }
-        updatePageSubtitle()
+    // Must contain org.futo.subscriptions.free_forever
+    fun getFreeSignupStages(flows: List<List<Stage>>): List<Stage>? = flows.firstOrNull { stages ->
+        stages.firstOrNull { stage ->
+            (stage as? Stage.Other)?.type == SUBSCRIPTION_FREE_TYPE
+        } != null
     }
 
-    private suspend fun handleStageOther(type: String): SignUpNavigationEvents? = when (type) {
-        REGISTRATION_FREE_TYPE -> {
-            performRegistrationStage(mapOf(BaseLoginStagesDataSource.TYPE_PARAM_KEY to REGISTRATION_FREE_TYPE))
-            null
-        }
+    // Must contain org.futo.subscription.google_play, available only for gPlay flavor
+    fun getSubscriptionSignupStages(flows: List<List<Stage>>): List<Stage>? =
+        if (CirclesAppConfig.isGplayFlavor()) {
+            flows.firstOrNull { stages ->
+                stages.firstOrNull { stage ->
+                    (stage as? Stage.Other)?.type == SUBSCRPTION_GOOGLE_TYPE
+                } != null
+            }
+        } else null
 
-        REGISTRATION_TOKEN_TYPE -> SignUpNavigationEvents.TokenValidation
-        REGISTRATION_SUBSCRIPTION_TYPE -> SignUpNavigationEvents.Subscription
-        REGISTRATION_EMAIL_REQUEST_TOKEN_TYPE -> SignUpNavigationEvents.ValidateEmail
-        REGISTRATION_EMAIL_SUBMIT_TOKEN_TYPE -> null
-        REGISTRATION_USERNAME_TYPE -> SignUpNavigationEvents.Username
-        REGISTRATION_PASSWORD_TYPE -> SignUpNavigationEvents.Password
-        REGISTRATION_BSSPEKE_OPRF_TYPE -> SignUpNavigationEvents.BSspeke
-        REGISTRATION_BSSPEKE_SAVE_TYPE -> null
-        else -> throw IllegalArgumentException(
-            context.getString(R.string.not_supported_stage_format, type)
-        )
-    }
-
-    private fun updatePageSubtitle() {
-        val size = stagesToComplete.size
-        val number = getCurrentStageIndex() + 1
-        val subtitle = context.getString(R.string.sign_up_stage_subtitle_format, number, size)
-        subtitleLiveData.postValue(subtitle)
-    }
-
-    companion object {
-        const val REGISTRATION_FREE_TYPE = "org.futo.subscriptions.free_forever"
-        const val REGISTRATION_TOKEN_TYPE = "m.login.registration_token"
-        const val REGISTRATION_SUBSCRIPTION_TYPE = "org.futo.subscriptions.google_play"
-        const val REGISTRATION_EMAIL_REQUEST_TOKEN_TYPE = "m.enroll.email.request_token"
-        const val REGISTRATION_EMAIL_SUBMIT_TOKEN_TYPE = "m.enroll.email.submit_token"
-        const val REGISTRATION_USERNAME_TYPE = "m.enroll.username"
-        const val REGISTRATION_PASSWORD_TYPE = "m.enroll.password"
-        const val REGISTRATION_BSSPEKE_OPRF_TYPE = "m.enroll.bsspeke-ecc.oprf"
-        const val REGISTRATION_BSSPEKE_SAVE_TYPE = "m.enroll.bsspeke-ecc.save"
-    }
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt
index 109d41325e342f245b40afc1654fb5ab8e645702..94e737616cfce75af621df96dbed4ce056fdb4b2 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt
@@ -2,102 +2,98 @@ package org.futo.circles.auth.feature.sign_up
 
 import android.os.Bundle
 import android.view.View
-import androidx.activity.OnBackPressedCallback
+import android.widget.RadioButton
+import androidx.core.view.children
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.NavHostFragment
 import androidx.navigation.fragment.findNavController
 import by.kirich1409.viewbindingdelegate.viewBinding
+import com.google.android.material.radiobutton.MaterialRadioButton
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentSignUpBinding
-import org.futo.circles.core.base.NetworkObserver
+import org.futo.circles.core.base.CirclesAppConfig
+import org.futo.circles.core.base.fragment.HasLoadingState
+import org.futo.circles.core.extensions.gone
 import org.futo.circles.core.extensions.navigateSafe
-import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
-import org.futo.circles.core.extensions.onBackPressed
-import org.futo.circles.core.extensions.setEnabledViews
-import org.futo.circles.core.extensions.showDialog
+import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.extensions.showError
-import org.futo.circles.core.base.fragment.BackPressOwner
-import org.futo.circles.core.view.LoadingDialog
 
 @AndroidEntryPoint
 class SignUpFragment : Fragment(R.layout.fragment_sign_up),
-    BackPressOwner {
+    HasLoadingState {
 
-    private val viewModel by viewModels<SignUpViewModel>()
-    private val binding by viewBinding(FragmentSignUpBinding::bind)
-    private val loadingDialog by lazy { LoadingDialog(requireContext()) }
+    override val fragment: Fragment = this
 
-    private val childNavHostFragment by lazy {
-        childFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
-    }
+    private val binding by viewBinding(FragmentSignUpBinding::bind)
+    private val viewModel by viewModels<SignUpViewModel>()
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        binding.toolbar.setNavigationOnClickListener { onBackPressed() }
+        setupViews()
         setupObservers()
     }
 
-    private fun setupObservers() {
-        NetworkObserver.observe(this){ setEnabledViews(it) }
-        viewModel.subtitleLiveData.observeData(this) {
-            binding.toolbar.subtitle = it
-        }
-        viewModel.navigationLiveData.observeData(this) {
-            handleNavigation(it)
-        }
-        viewModel.finishRegistrationLiveData.observeResponse(this,
-            success = { navigateToConfigureWorkspace() },
-            error = { message ->
-                showError(message)
-                loadingDialog.dismiss()
+    private fun setupViews() {
+        with(binding) {
+            serverDomainGroup.setOnCheckedChangeListener { _, _ ->
+                setFlowsLoading(true)
+                viewModel.loadSignupFlowsForDomain(getDomain())
+            }
+            CirclesAppConfig.serverDomains.forEach { domain ->
+                serverDomainGroup.addView(
+                    MaterialRadioButton(requireContext()).apply {
+                        text = domain
+                        textSize = 20f
+                    }
+                )
+            }
+            (serverDomainGroup.children.first() as? RadioButton)?.toggle()
+            btnSubscription.setOnClickListener {
+                startLoading(btnSubscription)
+                viewModel.startSignUp(true)
+            }
+            btnFree.setOnClickListener {
+                startLoading(btnFree)
+                viewModel.startSignUp(false)
             }
-        )
-        viewModel.passPhraseLoadingLiveData.observeData(this) {
-            loadingDialog.handleLoading(it)
         }
     }
 
+    private fun setupObservers() {
+        viewModel.startSignUpEventLiveData.observeResponse(
+            this,
+            success = { findNavController().navigateSafe(SignUpFragmentDirections.toUiaFragment()) }
+        )
 
-    private fun handleNavigation(event: SignUpNavigationEvents) {
-        val directionId = when (event) {
-            SignUpNavigationEvents.TokenValidation -> R.id.to_validateToken
-            SignUpNavigationEvents.Subscription -> R.id.to_subscriptions
-            SignUpNavigationEvents.AcceptTerm -> R.id.to_acceptTerms
-            SignUpNavigationEvents.ValidateEmail -> R.id.to_validateEmail
-            SignUpNavigationEvents.Password -> R.id.to_password
-            SignUpNavigationEvents.BSspeke -> R.id.to_bsspeke
-            SignUpNavigationEvents.Username -> R.id.to_username
-        }
-        binding.navHostFragment.findNavController().navigateSafe(directionId)
-    }
-
-    private fun showDiscardDialog() {
-        showDialog(
-            titleResIdRes = R.string.discard_current_registration_progress,
-            negativeButtonVisible = true,
-            positiveAction = {
-                childNavHostFragment.navController.popBackStack(
-                    R.id.selectSignUpTypeFragment, false
-                )
-            })
+        viewModel.signupFlowsLiveData.observeResponse(this,
+            success = {
+                val hasSubscriptionFlow = viewModel.hasSubscriptionFlow(it)
+                val hasFreeFlow = viewModel.hasFreeFlow(it)
+                with(binding) {
+                    btnSubscription.setIsVisible(hasSubscriptionFlow)
+                    btnFree.setIsVisible(hasFreeFlow)
+                    tvOr.setIsVisible(hasFreeFlow && hasSubscriptionFlow)
+                }
+            },
+            error = { message ->
+                showError(message)
+                binding.lButtonsContainer.gone()
+            },
+            onRequestInvoked = { setFlowsLoading(false) }
+        )
     }
 
-    override fun onChildBackPress(callback: OnBackPressedCallback) {
-        val includedFragmentsManager = childNavHostFragment.childFragmentManager
-        if (includedFragmentsManager.backStackEntryCount == 1) {
-            callback.remove()
-            onBackPressed()
-        } else {
-            showDiscardDialog()
+    private fun setFlowsLoading(isLoading: Boolean) {
+        with(binding) {
+            lButtonsContainer.setIsVisible(!isLoading)
+            flowProgress.setIsVisible(isLoading)
         }
     }
 
-    private fun navigateToConfigureWorkspace() {
-        findNavController().navigateSafe(SignUpFragmentDirections.toConfigureWorkspace())
-    }
+    private fun getDomain() =
+        binding.serverDomainGroup
+            .findViewById<MaterialRadioButton>(binding.serverDomainGroup.checkedRadioButtonId).text.toString()
 
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpViewModel.kt
index 039bc2140d3cb7fd01c5e9e704fb3e3cc87efc02..0c23ed8a0a043b2830e32e36e1c10f877bb52b15 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpViewModel.kt
@@ -2,16 +2,38 @@ package org.futo.circles.auth.feature.sign_up
 
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
+import org.futo.circles.core.base.SingleEventLiveData
+import org.futo.circles.core.extensions.Response
+import org.futo.circles.core.extensions.launchBg
+import org.matrix.android.sdk.api.auth.registration.Stage
 import javax.inject.Inject
 
 @HiltViewModel
 class SignUpViewModel @Inject constructor(
-    datasource: SignUpDataSource
+    private val dataSource: SignUpDataSource
 ) : ViewModel() {
 
-    val subtitleLiveData = datasource.subtitleLiveData
-    val passPhraseLoadingLiveData = datasource.passPhraseLoadingLiveData
-    val finishRegistrationLiveData = datasource.finishRegistrationLiveData
-    val navigationLiveData = datasource.navigationLiveData
+    val startSignUpEventLiveData = SingleEventLiveData<Response<Unit?>>()
+    val signupFlowsLiveData = SingleEventLiveData<Response<List<List<Stage>>>>()
+
+    fun startSignUp(isSubscription: Boolean) {
+        launchBg {
+            val result = dataSource.startNewRegistration(isSubscription)
+            startSignUpEventLiveData.postValue(result)
+        }
+    }
+
+    fun loadSignupFlowsForDomain(domain: String) {
+        launchBg {
+            val result = dataSource.getAuthFlowsFor(domain)
+            signupFlowsLiveData.postValue(result)
+        }
+    }
+
+    fun hasSubscriptionFlow(flows: List<List<Stage>>): Boolean =
+        dataSource.getSubscriptionSignupStages(flows) != null
+
+    fun hasFreeFlow(flows: List<List<Stage>>): Boolean =
+        dataSource.getFreeSignupStages(flows) != null
 
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/SignupBsSpekeDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/SignupBsSpekeDataSource.kt
deleted file mode 100644
index 808b059e9089ef4cd381e6869e395353e6f5fa23..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/SignupBsSpekeDataSource.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.password
-
-import android.content.Context
-import dagger.hilt.android.qualifiers.ApplicationContext
-import org.futo.circles.auth.base.BaseBsSpekeStageDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import org.matrix.android.sdk.api.auth.registration.Stage
-import org.matrix.android.sdk.api.util.JsonDict
-import javax.inject.Inject
-
-class SignupBsSpekeDataSource @Inject constructor(
-    @ApplicationContext context: Context,
-    private val signUpDataSource: SignUpDataSource
-) : BaseBsSpekeStageDataSource(context) {
-
-    override val userName: String get() = signUpDataSource.userName
-    override val domain: String get() = signUpDataSource.domain
-    override val isLoginMode: Boolean get() = false
-    override fun getStages(): List<Stage> = signUpDataSource.stagesToComplete
-
-    override suspend fun performAuthStage(
-        authParams: JsonDict,
-        password: String?
-    ): Response<RegistrationResult> = signUpDataSource.performRegistrationStage(authParams = authParams)
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/SignupPasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/SignupPasswordDataSource.kt
deleted file mode 100644
index 8f94a793ca309c3ce6e007538b03a8d7eecde36d..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/SignupPasswordDataSource.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.password
-
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.base.PasswordDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_PASSWORD_TYPE
-import org.futo.circles.core.extensions.Response
-import javax.inject.Inject
-
-class SignupPasswordDataSource @Inject constructor(
-    private val signUpDataSource: SignUpDataSource
-) : PasswordDataSource {
-
-    override suspend fun processPasswordStage(password: String): Response<Unit> =
-        when (val result = signUpDataSource.performRegistrationStage(
-            mapOf(
-                TYPE_PARAM_KEY to REGISTRATION_PASSWORD_TYPE,
-                PASSWORD_PARAM_KEY to password
-            )
-        )) {
-            is Response.Success -> Response.Success(Unit)
-            is Response.Error -> result
-        }
-
-    companion object {
-        private const val PASSWORD_PARAM_KEY = "new_password"
-    }
-}
\ 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
deleted file mode 100644
index 938a45db2087e51a2de9cf1f3cf01cdc0fb53c5e..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeDataSource.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-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.sign_up.SignUpDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_FREE_TYPE
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_SUBSCRIPTION_TYPE
-import org.futo.circles.core.base.CirclesAppConfig
-import org.futo.circles.core.extensions.createResult
-import org.futo.circles.core.provider.MatrixInstanceProvider
-import org.futo.circles.core.utils.HomeServerUtils.buildHomeServerConfigFromDomain
-import org.matrix.android.sdk.api.auth.registration.Stage
-import javax.inject.Inject
-
-class SelectSignUpTypeDataSource @Inject constructor(
-    @ApplicationContext private val context: Context,
-    private val signUpDataSource: SignUpDataSource
-) {
-
-    private var registrationFlowsForDomain: Pair<String, List<List<Stage>>>? = null
-
-    fun clearSubtitle() {
-        signUpDataSource.clearSubtitle()
-    }
-
-    suspend fun getAuthFlowsFor(domain: String) = createResult {
-        registrationFlowsForDomain = null
-        val authService = MatrixInstanceProvider.matrix.authenticationService().apply {
-            cancelPendingLoginOrRegistration()
-            initiateAuth(buildHomeServerConfigFromDomain(domain))
-        }
-        authService.getRegistrationWizard().getAllRegistrationFlows().also {
-            registrationFlowsForDomain = domain to it
-        }
-    }
-
-    suspend fun startNewRegistration(isSubscription: Boolean) = createResult {
-        val (domain, flows) = registrationFlowsForDomain ?: throw IllegalArgumentException(
-            context.getString(R.string.wrong_signup_config)
-        )
-        val stages = if (isSubscription) getSubscriptionSignupStages(flows)
-        else getFreeSignupStages(flows)
-
-        stages ?: throw IllegalArgumentException(context.getString(R.string.wrong_signup_config))
-
-        signUpDataSource.startSignUpStages(stages, domain)
-    }
-
-    // Must contain org.futo.subscriptions.free_forever
-    fun getFreeSignupStages(flows: List<List<Stage>>): List<Stage>? = flows.firstOrNull { stages ->
-        stages.firstOrNull { stage ->
-            (stage as? Stage.Other)?.type == REGISTRATION_FREE_TYPE
-        } != null
-    }
-
-    // Must contain org.futo.subscription.google_play, available only for gPlay flavor
-    fun getSubscriptionSignupStages(flows: List<List<Stage>>): List<Stage>? =
-        if (CirclesAppConfig.isGplayFlavor()) {
-            flows.firstOrNull { stages ->
-                stages.firstOrNull { stage ->
-                    (stage as? Stage.Other)?.type == REGISTRATION_SUBSCRIPTION_TYPE
-                } != null
-            }
-        } else null
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeFragment.kt
deleted file mode 100644
index 7c1ca617f958d823a1d579ec0285233bd8257585..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeFragment.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.sign_up_type
-
-import android.os.Bundle
-import android.view.View
-import androidx.core.view.children
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import by.kirich1409.viewbindingdelegate.viewBinding
-import com.google.android.material.radiobutton.MaterialRadioButton
-import dagger.hilt.android.AndroidEntryPoint
-import org.futo.circles.auth.R
-import org.futo.circles.auth.databinding.FragmentSelectSignUpTypeBinding
-import org.futo.circles.core.base.CirclesAppConfig
-import org.futo.circles.core.base.fragment.HasLoadingState
-import org.futo.circles.core.extensions.gone
-import org.futo.circles.core.extensions.observeResponse
-import org.futo.circles.core.extensions.setIsVisible
-import org.futo.circles.core.extensions.showError
-
-@AndroidEntryPoint
-class SelectSignUpTypeFragment : Fragment(R.layout.fragment_select_sign_up_type),
-    HasLoadingState {
-
-    override val fragment: Fragment = this
-
-    private val binding by viewBinding(FragmentSelectSignUpTypeBinding::bind)
-    private val viewModel by viewModels<SelectSignUpTypeViewModel>()
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-        viewModel.clearSubtitle()
-        setupViews()
-        setupObservers()
-    }
-
-    private fun setupViews() {
-        with(binding) {
-            serverDomainGroup.setOnCheckedChangeListener { _, _ ->
-                setFlowsLoading(true)
-                viewModel.loadSignupFlowsForDomain(getDomain())
-            }
-            CirclesAppConfig.serverDomains.forEach { domain ->
-                serverDomainGroup.addView(
-                    MaterialRadioButton(requireContext()).apply {
-                        text = domain
-                        textSize = 20f
-                    }
-                )
-            }
-            serverDomainGroup.check(serverDomainGroup.children.first().id)
-            btnSubscription.setOnClickListener {
-                startLoading(btnSubscription)
-                viewModel.startSignUp(true)
-            }
-            btnFree.setOnClickListener {
-                startLoading(btnFree)
-                viewModel.startSignUp(false)
-            }
-        }
-    }
-
-    private fun setupObservers() {
-        viewModel.startSignUpEventLiveData.observeResponse(this)
-        viewModel.signupFlowsLiveData.observeResponse(this,
-            success = {
-                val hasSubscriptionFlow = viewModel.hasSubscriptionFlow(it)
-                val hasFreeFlow = viewModel.hasFreeFlow(it)
-                with(binding) {
-                    btnSubscription.setIsVisible(hasSubscriptionFlow)
-                    btnFree.setIsVisible(hasFreeFlow)
-                    tvOr.setIsVisible(hasFreeFlow && hasSubscriptionFlow)
-                }
-            },
-            error = { message ->
-                showError(message)
-                binding.lButtonsContainer.gone()
-            },
-            onRequestInvoked = { setFlowsLoading(false) }
-        )
-    }
-
-    private fun setFlowsLoading(isLoading: Boolean) {
-        with(binding) {
-            lButtonsContainer.setIsVisible(!isLoading)
-            flowProgress.setIsVisible(isLoading)
-        }
-    }
-
-    private fun getDomain() =
-        binding.serverDomainGroup
-            .findViewById<MaterialRadioButton>(binding.serverDomainGroup.checkedRadioButtonId).text.toString()
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeViewModel.kt
deleted file mode 100644
index 677f8727cb2fc6f241b355066bfe02e445b74472..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/sign_up_type/SelectSignUpTypeViewModel.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.sign_up_type
-
-import androidx.lifecycle.ViewModel
-import dagger.hilt.android.lifecycle.HiltViewModel
-import org.futo.circles.core.base.SingleEventLiveData
-import org.futo.circles.core.extensions.Response
-import org.futo.circles.core.extensions.launchBg
-import org.matrix.android.sdk.api.auth.registration.Stage
-import javax.inject.Inject
-
-@HiltViewModel
-class SelectSignUpTypeViewModel @Inject constructor(
-    private val dataSource: SelectSignUpTypeDataSource
-) : ViewModel() {
-
-    val startSignUpEventLiveData = SingleEventLiveData<Response<Unit?>>()
-    val signupFlowsLiveData = SingleEventLiveData<Response<List<List<Stage>>>>()
-
-    fun startSignUp(isSubscription: Boolean) {
-        launchBg {
-            val result = dataSource.startNewRegistration(isSubscription)
-            startSignUpEventLiveData.postValue(result)
-        }
-    }
-
-    fun clearSubtitle() {
-        dataSource.clearSubtitle()
-    }
-
-    fun loadSignupFlowsForDomain(domain: String) {
-        launchBg {
-            val result = dataSource.getAuthFlowsFor(domain)
-            signupFlowsLiveData.postValue(result)
-        }
-    }
-
-    fun hasSubscriptionFlow(flows: List<List<Stage>>): Boolean =
-        dataSource.getSubscriptionSignupStages(flows) != null
-
-    fun hasFreeFlow(flows: List<List<Stage>>): Boolean =
-        dataSource.getFreeSignupStages(flows) != null
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/SignupAcceptTermsDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/SignupAcceptTermsDataSource.kt
deleted file mode 100644
index f4206731de46fef77712f1d6c84779bae1513571..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/SignupAcceptTermsDataSource.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.terms
-
-
-import org.futo.circles.auth.base.BaseAcceptTermsDataSource
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.extensions.toTermsListItems
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
-import org.matrix.android.sdk.api.auth.registration.Stage
-import javax.inject.Inject
-
-class SignupAcceptTermsDataSource @Inject constructor(
-    private val signUpDataSource: SignUpDataSource
-) : BaseAcceptTermsDataSource() {
-
-    override suspend fun acceptTerms(): Response<Unit> =
-        when (val result = signUpDataSource.performRegistrationStage(
-            mapOf(TYPE_PARAM_KEY to LoginFlowTypes.TERMS)
-        )) {
-            is Response.Success -> Response.Success(Unit)
-            is Response.Error -> result
-        }
-
-    override fun getTermsList() =
-        (signUpDataSource.currentStage as? Stage.Terms)?.policies?.toTermsListItems() ?: emptyList()
-
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameDataSource.kt
deleted file mode 100644
index bf64130222077532aa3ced8140c307748663cd4f..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameDataSource.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.username
-
-import androidx.lifecycle.MutableLiveData
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_USERNAME_TYPE
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import javax.inject.Inject
-
-class UsernameDataSource @Inject constructor(
-    private val signUpDataSource: SignUpDataSource
-) {
-
-    val domainLiveData = MutableLiveData(signUpDataSource.domain)
-
-    suspend fun processUsernameStage(username: String): Response<RegistrationResult> =
-        signUpDataSource.performRegistrationStage(
-            mapOf(
-                TYPE_PARAM_KEY to REGISTRATION_USERNAME_TYPE,
-                USERNAME_PARAM_KEY to username
-            ), name = username
-        )
-
-    companion object {
-        private const val USERNAME_PARAM_KEY = "username"
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailDataSource.kt
deleted file mode 100644
index 0ae5902e3a558d69cc1f9b39de75ce6b13951aac..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailDataSource.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.validate_email
-
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_EMAIL_REQUEST_TOKEN_TYPE
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_EMAIL_SUBMIT_TOKEN_TYPE
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import org.matrix.android.sdk.api.auth.registration.Stage
-import javax.inject.Inject
-
-class ValidateEmailDataSource @Inject constructor(
-    private val signUpDataSource: SignUpDataSource
-) {
-
-    suspend fun sendValidationCode(
-        email: String,
-        subscribeToUpdates: Boolean
-    ): Response<RegistrationResult> = signUpDataSource.performRegistrationStage(
-        mapOf(
-            TYPE_PARAM_KEY to REGISTRATION_EMAIL_REQUEST_TOKEN_TYPE,
-            EMAIL_PARAM_KEY to email,
-            SUBSCRIBE_TO_LIST to subscribeToUpdates
-        )
-    )
-
-    suspend fun validateEmail(code: String): Response<RegistrationResult> =
-        signUpDataSource.performRegistrationStage(
-            mapOf(
-                TYPE_PARAM_KEY to REGISTRATION_EMAIL_SUBMIT_TOKEN_TYPE,
-                TOKEN_PARAM_KEY to code
-            )
-        )
-
-    fun shouldShowSubscribeToEmail(): Boolean =
-        (signUpDataSource.currentStage as? Stage.Other)?.params?.get(OFFER_LIST_SUBSCRIPTION_KEY) as? Boolean
-            ?: false
-
-    companion object {
-        private const val EMAIL_PARAM_KEY = "email"
-        private const val TOKEN_PARAM_KEY = "token"
-        private const val SUBSCRIBE_TO_LIST = "subscribe_to_list"
-        private const val OFFER_LIST_SUBSCRIPTION_KEY = "offer_list_subscription"
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenDataSource.kt
deleted file mode 100644
index d77b10fd4a72868fe0f67176d8ef96183b8f749f..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenDataSource.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.futo.circles.auth.feature.sign_up.validate_token
-
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_TOKEN_TYPE
-import org.futo.circles.core.extensions.Response
-import org.matrix.android.sdk.api.auth.registration.RegistrationResult
-import javax.inject.Inject
-
-class ValidateTokenDataSource @Inject constructor(
-    private val signUpDataSource: SignUpDataSource
-) {
-
-    suspend fun validateToken(token: String): Response<RegistrationResult> =
-        signUpDataSource.performRegistrationStage(
-            mapOf(
-                TYPE_PARAM_KEY to REGISTRATION_TOKEN_TYPE,
-                TOKEN_PARAM_KEY to token
-            )
-        )
-
-    companion object {
-        private const val TOKEN_PARAM_KEY = "token"
-    }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..01a620e786cc656a7e39611d8fac2e55bf0cef0a
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSource.kt
@@ -0,0 +1,185 @@
+package org.futo.circles.auth.feature.uia
+
+import androidx.lifecycle.MutableLiveData
+import org.futo.circles.auth.feature.uia.flow.LoginStagesDataSource
+import org.futo.circles.auth.feature.uia.flow.SignUpStagesDataSource
+import org.futo.circles.auth.feature.uia.flow.reauth.ReAuthStagesDataSource
+import org.futo.circles.auth.model.UIAFlowType
+import org.futo.circles.auth.model.UIANavigationEvent
+import org.futo.circles.core.base.SingleEventLiveData
+import org.futo.circles.core.extensions.Response
+import org.matrix.android.sdk.api.auth.UIABaseAuth
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
+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 kotlin.coroutines.Continuation
+
+abstract class UIADataSource {
+
+    class Factory @Inject constructor(
+        private val loginStagesDataSource: LoginStagesDataSource,
+        private val reAuthStagesDataSource: ReAuthStagesDataSource,
+        private val signUpStagesDataSource: SignUpStagesDataSource
+    ) {
+        fun create(flowType: UIAFlowType): UIADataSource = when (flowType) {
+            UIAFlowType.Login -> loginStagesDataSource
+            UIAFlowType.Signup -> signUpStagesDataSource
+            UIAFlowType.ReAuth -> reAuthStagesDataSource
+            UIAFlowType.ForgotPassword -> loginStagesDataSource
+        }
+    }
+
+
+    val subtitleLiveData = MutableLiveData<Pair<Int, Int>>()
+    val stagesNavigationLiveData = SingleEventLiveData<UIANavigationEvent>()
+    val finishUIAEventLiveData = SingleEventLiveData<Session>()
+
+    val stagesToComplete = mutableListOf<Stage>()
+    var currentStage: Stage? = null
+        private set
+    var userName: String = ""
+        protected set
+    var domain: String = ""
+        private set
+
+
+    open suspend fun startUIAStages(
+        stages: List<Stage>,
+        serverDomain: String,
+        name: String? = null
+    ) {
+        currentStage = null
+        this.userName = name ?: ""
+        stagesToComplete.clear()
+        domain = serverDomain
+        stagesToComplete.addAll(stages)
+        navigateToNextStage()
+    }
+
+    open suspend fun startUIAStages(
+        stages: List<Stage>,
+        session: String,
+        promise: Continuation<UIABaseAuth>
+    ) {
+        throw IllegalArgumentException("Override only for AuthConfirmation provider usage")
+    }
+
+    abstract suspend fun performUIAStage(
+        authParams: JsonDict,
+        name: String? = null,
+        password: String? = null
+    ): Response<RegistrationResult>
+
+    open fun onStageResult(
+        promise: Continuation<UIABaseAuth>,
+        flowResponse: RegistrationFlowResponse,
+        errCode: String?
+    ) {
+        throw IllegalArgumentException("Override only for AuthConfirmation provider usage")
+    }
+
+
+    suspend fun stageCompleted(result: RegistrationResult) {
+        if (isStageRetry(result)) return
+        (result as? RegistrationResult.Success)?.let {
+            finishUIAEventLiveData.postValue(it.session)
+        } ?: navigateToNextStage()
+    }
+
+    fun getCurrentStageKey() = when (currentStage) {
+        is Stage.Other -> (currentStage as Stage.Other).type
+        else -> LoginFlowTypes.TERMS
+    }
+
+    private fun isStageRetry(result: RegistrationResult?): Boolean {
+        val nextStageType =
+            ((result as? RegistrationResult.FlowResponse)?.flowResult?.missingStages?.firstOrNull() as? Stage.Other)?.type
+        return nextStageType == (currentStage as? Stage.Other)?.type && nextStageType != null
+    }
+
+    private fun setNextStage() {
+        currentStage = currentStage?.let {
+            stagesToComplete.getOrNull(getCurrentStageIndex() + 1)
+        } ?: stagesToComplete.firstOrNull()
+    }
+
+    private suspend fun navigateToNextStage() {
+        setNextStage()
+        val event = when (val stage = currentStage) {
+            is Stage.Terms -> UIANavigationEvent.AcceptTerm
+            is Stage.Other -> handleStageOther(stage.type)
+            else -> throw IllegalArgumentException("Not supported stage $stage")
+        }
+        event?.let { stagesNavigationLiveData.postValue(it) }
+        updatePageSubtitle()
+    }
+
+
+    private suspend fun handleStageOther(type: String): UIANavigationEvent? = when (type) {
+        SUBSCRIPTION_FREE_TYPE -> {
+            performUIAStage(mapOf(TYPE_PARAM_KEY to type))
+            null
+        }
+
+        LOGIN_REGISTRATION_TOKEN_TYPE -> UIANavigationEvent.TokenValidation
+        SUBSCRPTION_GOOGLE_TYPE -> UIANavigationEvent.Subscription
+        LOGIN_EMAIL_REQUEST_TOKEN_TYPE,
+        ENROLL_EMAIL_REQUEST_TOKEN_TYPE -> UIANavigationEvent.ValidateEmail
+        LOGIN_EMAIL_SUBMIT_TOKEN_TYPE,
+        ENROLL_EMAIL_SUBMIT_TOKEN_TYPE -> null //stay on same screen
+        ENROLL_USERNAME_TYPE -> UIANavigationEvent.Username
+        ENROLL_PASSWORD_TYPE,
+        LOGIN_PASSWORD_TYPE,
+        LOGIN_BSSPEKE_OPRF_TYPE,
+        DIRECT_LOGIN_PASSWORD_TYPE,
+        ENROLL_BSSPEKE_OPRF_TYPE -> UIANavigationEvent.Password
+
+        ENROLL_BSSPEKE_SAVE_TYPE -> null
+        LOGIN_BSSPEKE_VERIFY_TYPE -> null
+        else -> throw IllegalArgumentException("Not supported stage $type")
+
+    }
+
+    private fun getCurrentStageIndex() =
+        stagesToComplete.indexOf(currentStage).takeIf { it != -1 } ?: 0
+
+    private fun updatePageSubtitle() {
+        val size = stagesToComplete.size
+        val number = getCurrentStageIndex() + 1
+        subtitleLiveData.postValue(number to size)
+    }
+
+    companion object {
+        //params
+        const val TYPE_PARAM_KEY = "type"
+        const val USER_PARAM_KEY = "user"
+        const val LOGIN_PASSWORD_USER_ID_TYPE = "m.id.user"
+
+        //stages password
+        const val LOGIN_PASSWORD_TYPE = "m.login.password"
+        const val DIRECT_LOGIN_PASSWORD_TYPE = "m.login.password.direct"
+        const val LOGIN_BSSPEKE_OPRF_TYPE = "m.login.bsspeke-ecc.oprf"
+        const val LOGIN_BSSPEKE_VERIFY_TYPE = "m.login.bsspeke-ecc.verify"
+        const val ENROLL_BSSPEKE_OPRF_TYPE = "m.enroll.bsspeke-ecc.oprf"
+        const val ENROLL_BSSPEKE_SAVE_TYPE = "m.enroll.bsspeke-ecc.save"
+
+        //stages subscription
+        const val SUBSCRIPTION_FREE_TYPE = "org.futo.subscriptions.free_forever"
+        const val SUBSCRPTION_GOOGLE_TYPE = "org.futo.subscriptions.google_play"
+
+        //stages email
+        const val LOGIN_EMAIL_REQUEST_TOKEN_TYPE = "m.login.email.request_token"
+        const val LOGIN_EMAIL_SUBMIT_TOKEN_TYPE = "m.login.email.submit_token"
+        const val ENROLL_EMAIL_REQUEST_TOKEN_TYPE = "m.enroll.email.request_token"
+        const val ENROLL_EMAIL_SUBMIT_TOKEN_TYPE = "m.enroll.email.submit_token"
+
+        const val LOGIN_REGISTRATION_TOKEN_TYPE = "m.login.registration_token"
+        const val ENROLL_USERNAME_TYPE = "m.enroll.username"
+        const val ENROLL_PASSWORD_TYPE = "m.enroll.password"
+
+    }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSourceProvider.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSourceProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2f8aeebf322290131e9951710da25ffca56e8ce5
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADataSourceProvider.kt
@@ -0,0 +1,26 @@
+package org.futo.circles.auth.feature.uia
+
+import org.futo.circles.auth.model.UIAFlowType
+
+
+object UIADataSourceProvider {
+
+    private var activeFlowDataSource: UIADataSource? = null
+
+    var activeFlowType: UIAFlowType? = null
+        private set
+
+    fun getDataSourceOrThrow() =
+        activeFlowDataSource ?: throw IllegalArgumentException("Flow is not active")
+
+
+    fun create(flowType: UIAFlowType, factory: UIADataSource.Factory): UIADataSource {
+        activeFlowType = flowType
+        return factory.create(flowType).also { activeFlowDataSource = it }
+    }
+
+    fun clear() {
+        activeFlowType = null
+        activeFlowDataSource = null
+    }
+}
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADialogFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADialogFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..046a9860c977b263ada3858e2c18f79e69590149
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIADialogFragment.kt
@@ -0,0 +1,220 @@
+package org.futo.circles.auth.feature.uia
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.net.Uri
+import android.os.Bundle
+import android.view.View
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.viewModels
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.NavHostFragment
+import androidx.navigation.fragment.findNavController
+import dagger.hilt.android.AndroidEntryPoint
+import org.futo.circles.auth.R
+import org.futo.circles.auth.databinding.DialogFragmentUiaBinding
+import org.futo.circles.auth.feature.pass_phrase.recovery.EnterPassPhraseDialog
+import org.futo.circles.auth.feature.pass_phrase.recovery.EnterPassPhraseDialogListener
+import org.futo.circles.auth.feature.uia.flow.reauth.ReAuthCancellationListener
+import org.futo.circles.auth.model.AuthUIAScreenNavigationEvent
+import org.futo.circles.auth.model.UIAFlowType
+import org.futo.circles.auth.model.UIANavigationEvent
+import org.futo.circles.core.base.NetworkObserver
+import org.futo.circles.core.base.fragment.BackPressOwner
+import org.futo.circles.core.base.fragment.BaseFullscreenDialogFragment
+import org.futo.circles.core.extensions.navigateSafe
+import org.futo.circles.core.extensions.observeData
+import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledViews
+import org.futo.circles.core.extensions.showDialog
+import org.futo.circles.core.extensions.showError
+import org.futo.circles.core.view.LoadingDialog
+
+@AndroidEntryPoint
+class UIADialogFragment :
+    BaseFullscreenDialogFragment(DialogFragmentUiaBinding::inflate), BackPressOwner {
+
+    private val viewModel by viewModels<UIAViewModel>()
+
+    private val binding by lazy {
+        getBinding() as DialogFragmentUiaBinding
+    }
+
+    private val loadingDialog by lazy { LoadingDialog(requireContext()) }
+    private var enterPassPhraseDialog: EnterPassPhraseDialog? = null
+
+    private val deviceIntentLauncher = registerForActivityResult(
+        ActivityResultContracts.GetContent()
+    ) { uri ->
+        uri ?: return@registerForActivityResult
+        enterPassPhraseDialog?.selectFile(uri)
+    }
+
+    private val childNavHostFragment by lazy {
+        childFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
+        object : Dialog(requireContext(), theme) {
+            @Suppress("OVERRIDE_DEPRECATION")
+            override fun onBackPressed() {
+                activity?.onBackPressedDispatcher?.onBackPressed()
+            }
+        }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        setupViews()
+        setupObservers()
+    }
+
+    override fun onDismiss(dialog: DialogInterface) {
+        super.onDismiss(dialog)
+        UIADataSourceProvider.clear()
+    }
+
+    private fun setupViews() {
+        binding.toolbar.apply {
+            title = getString(
+                when (UIADataSourceProvider.activeFlowType) {
+                    UIAFlowType.Login -> R.string.log_in
+                    UIAFlowType.Signup -> R.string.sign_up
+                    UIAFlowType.ReAuth -> R.string.confirm_auth
+                    UIAFlowType.ForgotPassword -> R.string.forgot_password
+                    else -> R.string.log_in
+                }
+            )
+            setNavigationOnClickListener { handleBackAction() }
+        }
+    }
+
+    private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it) }
+        viewModel.stagesNavigationLiveData.observeData(this) { event ->
+            handleStagesNavigation(event)
+        }
+        viewModel.navigationLiveData.observeData(this) { event ->
+            handleScreenNavigation(event)
+        }
+        viewModel.subtitleLiveData.observeData(this) { (number, size) ->
+            binding.toolbar.subtitle =
+                getString(R.string.sign_up_stage_subtitle_format, number, size)
+        }
+        viewModel.restoreKeysLiveData.observeResponse(
+            this,
+            error = {
+                showError(it)
+                loadingDialog.dismiss()
+            }
+        )
+        viewModel.passPhraseLoadingLiveData.observeData(this) {
+            loadingDialog.handleLoading(it)
+        }
+        viewModel.finishUIAEventLiveData.observeData(this) {
+            when (UIADataSourceProvider.activeFlowType) {
+                UIAFlowType.Login -> viewModel.finishLogin(it)
+                UIAFlowType.Signup -> viewModel.finishSignup(it)
+                UIAFlowType.ForgotPassword -> viewModel.finishForgotPassword(it)
+                else -> dismiss()
+            }
+        }
+        viewModel.createBackupResultLiveData.observeResponse(this,
+            error = { message ->
+                showError(message)
+                loadingDialog.dismiss()
+            }
+        )
+    }
+
+    private fun handleScreenNavigation(event: AuthUIAScreenNavigationEvent) {
+        when (event) {
+            AuthUIAScreenNavigationEvent.Home -> findNavController().navigateSafe(
+                UIADialogFragmentDirections.toHomeFragment()
+            )
+
+            AuthUIAScreenNavigationEvent.ConfigureWorkspace -> findNavController().navigateSafe(
+                UIADialogFragmentDirections.toConfigureWorkspace()
+            )
+
+            AuthUIAScreenNavigationEvent.PassPhrase -> showPassPhraseDialog()
+        }
+    }
+
+    private fun handleStagesNavigation(event: UIANavigationEvent) {
+        val id = when (event) {
+            UIANavigationEvent.TokenValidation -> R.id.to_validateToken
+            UIANavigationEvent.Subscription -> R.id.to_subscriptions
+            UIANavigationEvent.AcceptTerm -> R.id.to_acceptTerms
+            UIANavigationEvent.ValidateEmail -> R.id.to_validateEmail
+            UIANavigationEvent.Password -> R.id.to_password
+            UIANavigationEvent.Username -> R.id.to_username
+        }
+        binding.navHostFragment.findNavController().navigateSafe(id)
+    }
+
+    private fun showPassPhraseDialog() {
+        enterPassPhraseDialog =
+            EnterPassPhraseDialog(requireContext(), object : EnterPassPhraseDialogListener {
+                override fun onRestoreBackupWithPassphrase(passphrase: String) {
+                    viewModel.restoreBackupWithPassPhrase(passphrase)
+                }
+
+                override fun onRestoreBackupWithRawKey(key: String) {
+                    viewModel.restoreBackupWithRawKey(key)
+                }
+
+                override fun onRestoreBackup(uri: Uri) {
+                    viewModel.restoreBackup(uri)
+                }
+
+                override fun onDoNotRestore() {
+                    viewModel.cancelRestore()
+                }
+
+                override fun onSelectFileClicked() {
+                    deviceIntentLauncher.launch(recoveryKeyMimeType)
+                }
+            }).apply {
+                setOnDismissListener { enterPassPhraseDialog = null }
+                show()
+            }
+    }
+
+    private fun showDiscardDialog() {
+        showDialog(
+            titleResIdRes = R.string.discard_current_auth_progress,
+            negativeButtonVisible = true,
+            positiveAction = {
+                cancelReAuth()
+                findNavController().popBackStack()
+            }
+        )
+    }
+
+    override fun onChildBackPress(callback: OnBackPressedCallback) {
+        handleBackAction(callback)
+    }
+
+    private fun handleBackAction(callback: OnBackPressedCallback? = null) {
+        val includedFragmentsManager = childNavHostFragment.childFragmentManager
+        if (includedFragmentsManager.backStackEntryCount == 1) {
+            cancelReAuth()
+            callback?.remove()
+            onBackPressed()
+        } else {
+            showDiscardDialog()
+        }
+    }
+
+    private fun cancelReAuth() {
+        parentFragment?.childFragmentManager?.fragments?.forEach {
+            (it as? ReAuthCancellationListener)?.onReAuthCanceled()
+        }
+    }
+
+    companion object {
+        private const val recoveryKeyMimeType = "text/plain"
+    }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/UIAViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIAViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a4bb317ce31055db6b3f5235ac5eaec83fccb152
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/UIAViewModel.kt
@@ -0,0 +1,170 @@
+package org.futo.circles.auth.feature.uia
+
+import android.net.Uri
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.ViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+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.flow.LoginStagesDataSource
+import org.futo.circles.auth.model.AuthUIAScreenNavigationEvent
+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.extensions.launchBg
+import org.futo.circles.core.model.LoadingData
+import org.futo.circles.core.provider.MatrixInstanceProvider
+import org.futo.circles.core.provider.MatrixSessionProvider
+import org.futo.circles.core.provider.PreferencesProvider
+import org.matrix.android.sdk.api.auth.data.sessionId
+import org.matrix.android.sdk.api.session.Session
+import javax.inject.Inject
+
+@HiltViewModel
+class UIAViewModel @Inject constructor(
+    private val encryptionAlgorithmHelper: EncryptionAlgorithmHelper,
+    private val createPassPhraseDataSource: CreatePassPhraseDataSource,
+    private val restoreBackupDataSource: RestoreBackupDataSource,
+    private val refreshTokenManager: RefreshTokenManager,
+    private val preferencesProvider: PreferencesProvider
+) : ViewModel() {
+
+    val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+
+    val subtitleLiveData: LiveData<Pair<Int, Int>> = uiaDataSource.subtitleLiveData
+    val stagesNavigationLiveData = uiaDataSource.stagesNavigationLiveData
+    val navigationLiveData = SingleEventLiveData<AuthUIAScreenNavigationEvent>()
+    val restoreKeysLiveData = SingleEventLiveData<Response<Unit>>()
+    val passPhraseLoadingLiveData: MediatorLiveData<LoadingData> =
+        MediatorLiveData<LoadingData>().also {
+            it.addSource(restoreBackupDataSource.loadingLiveData) { value ->
+                it.postValue(value)
+            }
+            it.addSource(createPassPhraseDataSource.loadingLiveData) { value ->
+                it.postValue(value)
+            }
+        }
+    val finishUIAEventLiveData = uiaDataSource.finishUIAEventLiveData
+    val createBackupResultLiveData = SingleEventLiveData<Response<Unit>>()
+
+    fun restoreBackupWithPassPhrase(passphrase: String) {
+        launchBg {
+            val restoreResult = createResult {
+                restoreBackupDataSource.restoreKeysWithPassPhase(passphrase)
+            }
+            handleRestoreResult(restoreResult)
+        }
+    }
+
+    fun restoreBackupWithRawKey(rawKey: String) {
+        launchBg {
+            val restoreResult = createResult {
+                restoreBackupDataSource.restoreKeysWithRawKey(rawKey)
+            }
+            handleRestoreResult(restoreResult)
+        }
+    }
+
+    fun restoreBackup(uri: Uri) {
+        launchBg {
+            val restoreResult = createResult {
+                restoreBackupDataSource.restoreKeysWithRecoveryKey(uri)
+            }
+            handleRestoreResult(restoreResult)
+        }
+    }
+
+    fun finishLogin(session: Session) {
+        launchBg {
+            passPhraseLoadingLiveData.postValue(
+                LoadingData(messageId = R.string.initial_sync, isLoading = true)
+            )
+            MatrixSessionProvider.awaitForSessionSync(session)
+            passPhraseLoadingLiveData.postValue(LoadingData(isLoading = false))
+            refreshTokenManager.scheduleTokenRefreshIfNeeded(session)
+            handleKeysBackup()
+            BSSpekeClientProvider.clear()
+        }
+    }
+
+    fun finishSignup(session: Session) {
+        launchBg {
+            val result = createResult {
+                MatrixInstanceProvider.matrix.authenticationService().reset()
+                MatrixSessionProvider.awaitForSessionStart(session)
+                preferencesProvider.setShouldShowAllExplanations()
+                createPassPhraseDataSource.createPassPhraseBackup()
+                BSSpekeClientProvider.clear()
+            }
+            (result as? Response.Success)?.let {
+                navigationLiveData.postValue(AuthUIAScreenNavigationEvent.ConfigureWorkspace)
+            }
+            createBackupResultLiveData.postValue(result)
+        }
+    }
+
+    fun finishForgotPassword(session: Session) {
+        launchBg {
+            val result = createResult {
+                MatrixSessionProvider.awaitForSessionSync(session)
+                createPassPhraseDataSource.createPassPhraseBackup()
+                BSSpekeClientProvider.clear()
+            }
+            (result as? Response.Success)?.let {
+                navigationLiveData.postValue(AuthUIAScreenNavigationEvent.Home)
+            }
+            createBackupResultLiveData.postValue(result)
+        }
+    }
+
+    private suspend fun handleKeysBackup() {
+        if (encryptionAlgorithmHelper.isBcryptAlgorithm()) {
+            (uiaDataSource as? LoginStagesDataSource)?.userPassword?.let {
+                restoreAndMigrateBCrypt(it)
+            }
+        } else {
+            if (encryptionAlgorithmHelper.isBsSpekePassPhrase()) restoreBsSpekeBackup()
+            else navigationLiveData.postValue(AuthUIAScreenNavigationEvent.PassPhrase)
+        }
+    }
+
+    private suspend fun restoreBsSpekeBackup() {
+        val restoreResult = createResult { restoreBackupDataSource.restoreWithBsSpekeKey() }
+        handleRestoreResult(restoreResult)
+    }
+
+    private suspend fun restoreAndMigrateBCrypt(passphrase: String) {
+        val restoreResult = createResult {
+            restoreBackupDataSource.restoreBcryptWithPassPhase(passphrase)
+            createPassPhraseDataSource.replaceToNewKeyBackup()
+        }
+        handleRestoreResult(restoreResult)
+    }
+
+    private fun handleRestoreResult(restoreResult: Response<Unit>) {
+        when (restoreResult) {
+            is Response.Error -> {
+                restoreKeysLiveData.postValue(restoreResult)
+                navigationLiveData.postValue(AuthUIAScreenNavigationEvent.PassPhrase)
+            }
+
+            is Response.Success -> navigationLiveData.postValue(AuthUIAScreenNavigationEvent.Home)
+        }
+    }
+
+    fun cancelRestore() {
+        launchBg {
+            val session = MatrixSessionProvider.currentSession ?: return@launchBg
+            val sessionId = session.sessionParams.credentials.sessionId()
+            refreshTokenManager.cancelTokenRefreshing(session)
+            MatrixSessionProvider.removeListenersAndStopSync()
+            MatrixInstanceProvider.matrix.authenticationService().removeSession(sessionId)
+        }
+    }
+
+}
\ No newline at end of file
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..b685d27820567599e3f9547e7ccc0515022c9bd6
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/LoginStagesDataSource.kt
@@ -0,0 +1,59 @@
+package org.futo.circles.auth.feature.uia.flow
+
+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.core.extensions.Response
+import org.futo.circles.core.extensions.createResult
+import org.futo.circles.core.provider.MatrixInstanceProvider
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import org.matrix.android.sdk.api.auth.registration.Stage
+import org.matrix.android.sdk.api.util.JsonDict
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class LoginStagesDataSource @Inject constructor(
+    @ApplicationContext private val context: Context
+) : UIADataSource() {
+
+
+    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
+    }
+
+    private fun getIdentifier() = mapOf(
+        USER_PARAM_KEY to "@$userName:$domain",
+        TYPE_PARAM_KEY to LOGIN_PASSWORD_USER_ID_TYPE
+    )
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/SignUpStagesDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/SignUpStagesDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c9fb31553f36ef81b24c8c19ddf8dc7127308a19
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/SignUpStagesDataSource.kt
@@ -0,0 +1,41 @@
+package org.futo.circles.auth.feature.uia.flow
+
+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.core.extensions.Response
+import org.futo.circles.core.extensions.createResult
+import org.futo.circles.core.provider.MatrixInstanceProvider
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import org.matrix.android.sdk.api.util.JsonDict
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class SignUpStagesDataSource @Inject constructor(
+    @ApplicationContext private val context: Context
+) : UIADataSource() {
+
+    override suspend fun performUIAStage(
+        authParams: JsonDict,
+        name: String?,
+        password: String?
+    ): Response<RegistrationResult> {
+        val wizard = MatrixInstanceProvider.matrix.authenticationService().getRegistrationWizard()
+        val result = createResult {
+            wizard.registrationCustom(
+                authParams,
+                context.getString(R.string.initial_device_name),
+                true
+            )
+        }
+
+        (result as? Response.Success)?.let {
+            name?.let { userName = it }
+            stageCompleted(result.data)
+        }
+        return result
+    }
+
+}
\ No newline at end of file
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/uia/flow/reauth/AuthConfirmationProvider.kt
similarity index 55%
rename from auth/src/main/java/org/futo/circles/auth/feature/reauth/AuthConfirmationProvider.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/AuthConfirmationProvider.kt
index 4e83420c6c358204739b5a791f22e6d2b6d87817..1df07df5019cd8e75ee8c2c649b2d0a8c14311f4 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/reauth/AuthConfirmationProvider.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/AuthConfirmationProvider.kt
@@ -1,6 +1,13 @@
-package org.futo.circles.auth.feature.reauth
+package org.futo.circles.auth.feature.uia.flow.reauth
 
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.futo.circles.auth.feature.uia.UIADataSource
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.auth.model.UIAFlowType
 import org.futo.circles.core.base.SingleEventLiveData
+import org.futo.circles.core.extensions.coroutineScope
+import org.futo.circles.core.provider.MatrixSessionProvider
 import org.matrix.android.sdk.api.auth.UIABaseAuth
 import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
 import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
@@ -10,7 +17,7 @@ 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>()
@@ -24,9 +31,13 @@ class AuthConfirmationProvider @Inject constructor(
         if (flowResponse.completedStages.isNullOrEmpty()) {
             val stages = flowResponse.toFlowsWithStages().firstOrNull() ?: emptyList()
             startReAuthEventLiveData.postValue(Unit)
-            reAuthStagesDataSource.startReAuthStages(flowResponse.session ?: "", stages, promise)
+            MatrixSessionProvider.getSessionOrThrow().coroutineScope.launch(Dispatchers.IO) {
+                UIADataSourceProvider.create(UIAFlowType.ReAuth, uiaFactory)
+                    .apply { startUIAStages(stages, flowResponse.session ?: "", promise) }
+            }
         } else {
-            reAuthStagesDataSource.onStageResult(promise, flowResponse, errCode)
+            UIADataSourceProvider.getDataSourceOrThrow()
+                .onStageResult(promise, flowResponse, errCode)
         }
     }
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthCancellationListener.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthCancellationListener.kt
similarity index 55%
rename from auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthCancellationListener.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthCancellationListener.kt
index a5fbeb54c1fe763b85796266584b1a224006cf86..fbd4b1b23e236d5901437d6c736cc87c24a853cf 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthCancellationListener.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthCancellationListener.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.reauth
+package org.futo.circles.auth.feature.uia.flow.reauth
 
 interface ReAuthCancellationListener {
     fun onReAuthCanceled()
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStagesDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthStagesDataSource.kt
similarity index 69%
rename from auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStagesDataSource.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthStagesDataSource.kt
index 8beada3ac14f0d272b5d22c93824521ab1592437..77ad13a926862e13227772eb71f3e7a2ebe45056 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/reauth/ReAuthStagesDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/flow/reauth/ReAuthStagesDataSource.kt
@@ -1,10 +1,7 @@
-package org.futo.circles.auth.feature.reauth
+package org.futo.circles.auth.feature.uia.flow.reauth
 
-import android.content.Context
-import dagger.hilt.android.qualifiers.ApplicationContext
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
+import org.futo.circles.auth.feature.uia.UIADataSource
 import org.futo.circles.auth.model.CustomUIAuth
-import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.provider.MatrixSessionProvider
 import org.matrix.android.sdk.api.auth.UIABaseAuth
@@ -20,18 +17,15 @@ import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
 
 @Singleton
-class ReAuthStagesDataSource @Inject constructor(
-    @ApplicationContext context: Context,
-) : BaseLoginStagesDataSource(context) {
+class ReAuthStagesDataSource @Inject constructor() : UIADataSource() {
 
-    val finishReAuthEventLiveData = SingleEventLiveData<Unit>()
     private var authPromise: Continuation<UIABaseAuth>? = null
     private var sessionId: String = ""
     private var stageResultContinuation: Continuation<Response<RegistrationResult>>? = null
 
-    fun startReAuthStages(
+    override suspend fun startUIAStages(
+        stages: List<Stage>,
         session: String,
-        loginStages: List<Stage>,
         promise: Continuation<UIABaseAuth>
     ) {
         sessionId = session
@@ -40,11 +34,13 @@ class ReAuthStagesDataSource @Inject constructor(
         val userId = MatrixSessionProvider.getSessionOrThrow().myUserId
         val domain = userId.substringAfter(":")
         val name = userId.substringAfter("@").substringBefore(":")
-        super.startLoginStages(loginStages, name, domain)
+        startUIAStages(stages, domain, name)
     }
 
-    override suspend fun performLoginStage(
+
+    override suspend fun performUIAStage(
         authParams: JsonDict,
+        name: String?,
         password: String?
     ): Response<RegistrationResult> {
         authPromise?.resume(CustomUIAuth(sessionId, authParams))
@@ -54,13 +50,12 @@ class ReAuthStagesDataSource @Inject constructor(
         else awaitForStageResult()
 
         (result as? Response.Success)?.let {
-            password?.let { userPassword = it }
             stageCompleted(it.data)
-        } ?: run { finishReAuthEventLiveData.postValue(Unit) }
+        } ?: run { finishUIAEventLiveData.postValue(MatrixSessionProvider.getSessionOrThrow()) }
         return result
     }
 
-    fun onStageResult(
+    override fun onStageResult(
         promise: Continuation<UIABaseAuth>,
         flowResponse: RegistrationFlowResponse,
         errCode: String?
@@ -72,14 +67,8 @@ class ReAuthStagesDataSource @Inject constructor(
         stageResultContinuation?.resume(result)
     }
 
-    private fun stageCompleted(result: RegistrationResult) {
-        if (isStageRetry(result)) return
-        (result as? RegistrationResult.Success)?.let {
-            finishReAuthEventLiveData.postValue(Unit)
-        } ?: navigateToNextStage()
-    }
-
     private suspend fun awaitForStageResult() = suspendCoroutine { stageResultContinuation = it }
 
     private fun isLastStage() = stagesToComplete.last() == currentStage
+
 }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/EmptyFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/EmptyFragment.kt
similarity index 72%
rename from auth/src/main/java/org/futo/circles/auth/feature/log_in/EmptyFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/EmptyFragment.kt
index 65bf991491b3da367e9732f0689a24506e55c2aa..ffdd3e0472343eae8b8e9cfa6c3ef568fc3885f6 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/EmptyFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/EmptyFragment.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.log_in
+package org.futo.circles.auth.feature.uia.stages
 
 import androidx.fragment.app.Fragment
 import org.futo.circles.auth.R
diff --git a/auth/src/main/java/org/futo/circles/auth/base/BaseBsSpekeStageDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/BsSpekeStageDataSource.kt
similarity index 73%
rename from auth/src/main/java/org/futo/circles/auth/base/BaseBsSpekeStageDataSource.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/BsSpekeStageDataSource.kt
index 01e9b0b4a33c15507e09748c1e9ce2575feeaa63..fb2a8b92813fdbcb858298e6775c518c8152755d 100644
--- a/auth/src/main/java/org/futo/circles/auth/base/BaseBsSpekeStageDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/BsSpekeStageDataSource.kt
@@ -1,46 +1,34 @@
-package org.futo.circles.auth.base
+package org.futo.circles.auth.feature.uia.stages.password
 
 import android.content.Context
 import android.util.Base64
+import dagger.hilt.android.qualifiers.ApplicationContext
 import org.futo.circles.auth.R
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.LOGIN_BSSPEKE_OPRF_TYPE
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.LOGIN_BSSPEKE_VERIFY_TYPE
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
 import org.futo.circles.auth.bsspeke.BSSpekeClient
 import org.futo.circles.auth.bsspeke.BSSpekeClientProvider
-import org.futo.circles.auth.feature.log_in.stages.password.LoginBsSpekeDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_BSSPEKE_OPRF_TYPE
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_BSSPEKE_SAVE_TYPE
-import org.futo.circles.auth.feature.sign_up.password.SignupBsSpekeDataSource
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_BSSPEKE_OPRF_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_BSSPEKE_SAVE_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_BSSPEKE_OPRF_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_BSSPEKE_VERIFY_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
 import org.futo.circles.core.extensions.Response
 import org.matrix.android.sdk.api.auth.registration.RegistrationResult
 import org.matrix.android.sdk.api.auth.registration.Stage
-import org.matrix.android.sdk.api.util.JsonDict
 import javax.inject.Inject
 
-abstract class BaseBsSpekeStageDataSource(private val context: Context) : PasswordDataSource {
+class BsSpekeStageDataSource @Inject constructor(
+    @ApplicationContext private val context: Context
+) {
 
-    class Factory @Inject constructor(
-        private val loginBsSpekeDataSourceFactory: LoginBsSpekeDataSource.Factory,
-        private val signupBsSpekeDataSource: SignupBsSpekeDataSource
-    ) {
-        fun create(isSignup: Boolean, isReAuth: Boolean, isChangePasswordEnroll: Boolean) =
-            if (isSignup) signupBsSpekeDataSource
-            else loginBsSpekeDataSourceFactory.create(isReAuth, isChangePasswordEnroll)
-    }
-
-    protected abstract val userName: String
-    protected abstract val domain: String
-    protected abstract val isLoginMode: Boolean
-    protected abstract fun getStages(): List<Stage>
-    protected abstract suspend fun performAuthStage(
-        authParams: JsonDict,
-        password: String? = null
-    ): Response<RegistrationResult>
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
 
+    private var isVerifyStages = true
 
-    override suspend fun processPasswordStage(password: String): Response<Unit> {
-        BSSpekeClientProvider.initClient(userName, domain, password)
+    suspend fun processPasswordStage(password: String): Response<Unit> {
+        val currentStageKey = uiaDataSource.getCurrentStageKey()
+        isVerifyStages = currentStageKey == LOGIN_BSSPEKE_OPRF_TYPE || currentStageKey== LOGIN_BSSPEKE_VERIFY_TYPE
+        BSSpekeClientProvider.initClient(uiaDataSource.userName, uiaDataSource.domain, password)
         val bsSpekeClient = BSSpekeClientProvider.getClientOrThrow()
         return when (val oprfResult = performOprfStage(bsSpekeClient)) {
             is Response.Success -> processOprfSuccessResponse(
@@ -51,12 +39,12 @@ abstract class BaseBsSpekeStageDataSource(private val context: Context) : Passwo
         }
     }
 
-    private fun getOprfTypeKey() =
-        if (isLoginMode) LOGIN_BSSPEKE_OPRF_TYPE else REGISTRATION_BSSPEKE_OPRF_TYPE
+    private fun getOprfTypeKey() = if (isVerifyStages) LOGIN_BSSPEKE_OPRF_TYPE
+    else ENROLL_BSSPEKE_OPRF_TYPE
 
     private suspend fun performOprfStage(
         bsSpekeClient: BSSpekeClient
-    ): Response<RegistrationResult> = performAuthStage(
+    ): Response<RegistrationResult> = uiaDataSource.performUIAStage(
         mapOf(
             TYPE_PARAM_KEY to getOprfTypeKey(),
             CURVE_PARAM_KEY to getCurve(),
@@ -79,7 +67,7 @@ abstract class BaseBsSpekeStageDataSource(private val context: Context) : Passwo
             return Response.Error(e.message ?: "")
         }
         return when (val result =
-            if (isLoginMode) performVerifyStage(
+            if (isVerifyStages) performVerifyStage(
                 oprfResult, bsSpekeClient, password, blocks, iterations
             )
             else performSaveStage(
@@ -100,14 +88,14 @@ abstract class BaseBsSpekeStageDataSource(private val context: Context) : Passwo
     ): Response<RegistrationResult> {
         val PandV: Pair<String, String>
         try {
-            val blindSalt = getBlindSalt(context, oprfResult, REGISTRATION_BSSPEKE_SAVE_TYPE)
+            val blindSalt = getBlindSalt(context, oprfResult, ENROLL_BSSPEKE_SAVE_TYPE)
             PandV = bsSpekeClient.generateBase64PandV(blindSalt, blocks, iterations)
         } catch (e: Exception) {
             return Response.Error(e.message ?: "")
         }
-        return performAuthStage(
+        return uiaDataSource.performUIAStage(
             mapOf(
-                TYPE_PARAM_KEY to REGISTRATION_BSSPEKE_SAVE_TYPE,
+                TYPE_PARAM_KEY to ENROLL_BSSPEKE_SAVE_TYPE,
                 P_PARAM_KEY to PandV.first,
                 V_PARAM_KEY to PandV.second,
                 PHF_PARAM_KEY to phfParams
@@ -133,7 +121,7 @@ abstract class BaseBsSpekeStageDataSource(private val context: Context) : Passwo
         } catch (e: Exception) {
             return Response.Error(e.message ?: "")
         }
-        return performAuthStage(
+        return uiaDataSource.performUIAStage(
             mapOf(
                 TYPE_PARAM_KEY to LOGIN_BSSPEKE_VERIFY_TYPE,
                 A_PARAM_KEY to A,
@@ -196,11 +184,11 @@ abstract class BaseBsSpekeStageDataSource(private val context: Context) : Passwo
     }
 
     private fun getOprfStage(): Stage? =
-        getStages().firstOrNull { (it as? Stage.Other)?.type == getOprfTypeKey() }
+        uiaDataSource.stagesToComplete.firstOrNull { (it as? Stage.Other)?.type == getOprfTypeKey() }
 
     companion object {
-        const val CURVE_PARAM_KEY = "curve"
-        const val PHF_PARAM_KEY = "phf_params"
+        private const val CURVE_PARAM_KEY = "curve"
+        private const val PHF_PARAM_KEY = "phf_params"
         private const val BLIND_SALT_PARAM_KEY = "blind_salt"
         private const val PHF_ITERATIONS_PARAM_KEY = "iterations"
         private const val PHF_BLOCKS_PARAM_KEY = "blocks"
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..99ed81dedb96b8f6a0be9f9b326a8a6e3d4fc3cc
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt
@@ -0,0 +1,90 @@
+package org.futo.circles.auth.feature.uia.stages.password
+
+import android.content.Context
+import dagger.hilt.android.qualifiers.ApplicationContext
+import org.futo.circles.auth.R
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.DIRECT_LOGIN_PASSWORD_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_BSSPEKE_OPRF_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_PASSWORD_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_BSSPEKE_OPRF_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_BSSPEKE_SAVE_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_PASSWORD_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_BSSPEKE_VERIFY_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.core.extensions.Response
+import org.futo.circles.core.extensions.createResult
+import org.futo.circles.core.provider.MatrixInstanceProvider
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import org.matrix.android.sdk.api.auth.registration.Stage
+import javax.inject.Inject
+
+class PasswordDataSource @Inject constructor(
+    @ApplicationContext private val context: Context,
+    private val bsSpekeStageDataSource: BsSpekeStageDataSource
+) {
+
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+
+    suspend fun processPasswordStage(password: String): Response<Unit> =
+        when (uiaDataSource.getCurrentStageKey()) {
+            LOGIN_BSSPEKE_OPRF_TYPE, ENROLL_BSSPEKE_OPRF_TYPE,
+            LOGIN_BSSPEKE_VERIFY_TYPE, ENROLL_BSSPEKE_SAVE_TYPE->
+                bsSpekeStageDataSource.processPasswordStage(password)
+
+            ENROLL_PASSWORD_TYPE -> processRegistrationPasswordStage(password)
+            LOGIN_PASSWORD_TYPE -> processPasswordStageL(password)
+            DIRECT_LOGIN_PASSWORD_TYPE -> processDirectPasswordStage(password)
+            else -> throw IllegalArgumentException("Unsupported password stage")
+        }
+
+
+    private suspend fun processRegistrationPasswordStage(password: String): Response<Unit> =
+        when (val result = uiaDataSource.performUIAStage(
+            mapOf(
+                TYPE_PARAM_KEY to ENROLL_PASSWORD_TYPE,
+                REGISTRATION_PASSWORD_PARAM_KEY to password
+            )
+        )) {
+            is Response.Success -> Response.Success(Unit)
+            is Response.Error -> result
+        }
+
+
+    private suspend fun processPasswordStageL(password: String): Response<Unit> {
+        val result = uiaDataSource.performUIAStage(
+            mapOf(
+                TYPE_PARAM_KEY to LOGIN_PASSWORD_TYPE,
+                LOGIN_PASSWORD_PARAM_KEY to password
+            ), password
+        )
+        return when (result) {
+            is Response.Success -> Response.Success(Unit)
+            is Response.Error -> result
+        }
+    }
+
+    private suspend fun processDirectPasswordStage(password: String): Response<Unit> {
+        val result = createResult {
+            MatrixInstanceProvider.matrix.authenticationService().getLoginWizard().login(
+                login = uiaDataSource.userName,
+                password = password,
+                initialDeviceName = context.getString(R.string.initial_device_name)
+            )
+        }
+        return when (result) {
+            is Response.Success -> {
+                uiaDataSource.stageCompleted(RegistrationResult.Success(result.data))
+                Response.Success(Unit)
+            }
+
+            is Response.Error -> result
+        }
+    }
+
+    companion object {
+        private const val REGISTRATION_PASSWORD_PARAM_KEY = "new_password"
+        private const val LOGIN_PASSWORD_PARAM_KEY = "password"
+    }
+
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/PasswordFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt
similarity index 83%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/PasswordFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt
index 58b8ad2611c21bd0e942a3a9161ba40141b2dafc..a021c6bfc587ef3088a1e67dda08751d16e8f24b 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/PasswordFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.password
+package org.futo.circles.auth.feature.uia.stages.password
 
 import android.os.Bundle
 import android.view.View
@@ -6,13 +6,13 @@ import android.view.inputmethod.EditorInfo
 import androidx.core.widget.doAfterTextChanged
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.navArgs
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentPasswordBinding
-import org.futo.circles.auth.feature.sign_up.password.confirmation.SetupPasswordWarningDialog
-import org.futo.circles.auth.model.PasswordModeArg
+import org.futo.circles.auth.feature.uia.UIADataSource
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.auth.feature.uia.stages.password.confirmation.SetupPasswordWarningDialog
 import org.futo.circles.core.base.fragment.HasLoadingState
 import org.futo.circles.core.base.fragment.ParentBackPressOwnerFragment
 import org.futo.circles.core.extensions.getText
@@ -24,7 +24,6 @@ import org.futo.circles.core.extensions.showError
 class PasswordFragment : ParentBackPressOwnerFragment(R.layout.fragment_password),
     HasLoadingState {
 
-    private val args: PasswordFragmentArgs by navArgs()
     private val viewModel by viewModels<PasswordViewModel>()
     override val fragment: Fragment = this
     private val binding by viewBinding(FragmentPasswordBinding::bind)
@@ -64,9 +63,11 @@ class PasswordFragment : ParentBackPressOwnerFragment(R.layout.fragment_password
         }
     }
 
-    private fun isSignupMode() = when (args.mode) {
-        PasswordModeArg.ReAuthBsSpekeSignup, PasswordModeArg.SignupPasswordStage, PasswordModeArg.SignupBsSpekeStage -> true
-        else -> false
+    private fun isSignupMode(): Boolean {
+        val currentStageKey = UIADataSourceProvider.getDataSourceOrThrow().getCurrentStageKey()
+        return currentStageKey == UIADataSource.ENROLL_PASSWORD_TYPE
+                || currentStageKey == UIADataSource.ENROLL_BSSPEKE_OPRF_TYPE
+                || currentStageKey == UIADataSource.ENROLL_BSSPEKE_SAVE_TYPE
     }
 
     private fun setupObservers() {
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/PasswordViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt
similarity index 88%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/PasswordViewModel.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt
index 21c50f808114feaf4eb8af63c3e5c35bd93b9017..6db8e20ddddcf50156835b352fa6126f7d994435 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/PasswordViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt
@@ -1,8 +1,7 @@
-package org.futo.circles.auth.feature.sign_up.password
+package org.futo.circles.auth.feature.uia.stages.password
 
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
-import org.futo.circles.auth.base.PasswordDataSource
 import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.launchBg
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/confirmation/SetupPasswordWarningDialog.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/confirmation/SetupPasswordWarningDialog.kt
similarity index 93%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/confirmation/SetupPasswordWarningDialog.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/confirmation/SetupPasswordWarningDialog.kt
index c82824761cc92a1bb1023325a7073fbf061817d4..3c3f4c0f62e37b77404b078ae96d12f1f475b0bd 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/password/confirmation/SetupPasswordWarningDialog.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/confirmation/SetupPasswordWarningDialog.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.password.confirmation
+package org.futo.circles.auth.feature.uia.stages.password.confirmation
 
 import android.app.ActionBar
 import android.content.Context
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageDataSource.kt
similarity index 65%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageDataSource.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageDataSource.kt
index c5504441bd078e6d26374535a7fe74a13e051dba..d1601e52eebcd79a076c6726f06c70f34add04ba 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageDataSource.kt
@@ -1,24 +1,24 @@
-package org.futo.circles.auth.feature.sign_up.subscription_stage
+package org.futo.circles.auth.feature.uia.stages.subscription_stage
 
-import org.futo.circles.auth.base.BaseLoginStagesDataSource.Companion.TYPE_PARAM_KEY
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource
-import org.futo.circles.auth.feature.sign_up.SignUpDataSource.Companion.REGISTRATION_SUBSCRIPTION_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.SUBSCRPTION_GOOGLE_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
 import org.futo.circles.auth.model.SubscriptionReceiptData
 import org.futo.circles.core.extensions.Response
 import org.matrix.android.sdk.api.auth.registration.RegistrationResult
 import org.matrix.android.sdk.api.auth.registration.Stage
 import javax.inject.Inject
 
-class SubscriptionStageDataSource @Inject constructor(
-    private val signUpDataSource: SignUpDataSource
-) {
+class SubscriptionStageDataSource @Inject constructor() {
+
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
 
     suspend fun validateSubscription(
         subscriptionReceiptData: SubscriptionReceiptData
     ): Response<RegistrationResult> =
-        signUpDataSource.performRegistrationStage(
+        uiaDataSource.performUIAStage(
             mapOf(
-                TYPE_PARAM_KEY to REGISTRATION_SUBSCRIPTION_TYPE,
+                TYPE_PARAM_KEY to SUBSCRPTION_GOOGLE_TYPE,
                 ORDER_ID_KEY to subscriptionReceiptData.orderId,
                 PACKAGE_KEY to subscriptionReceiptData.packageName,
                 SUBSCRIPTION_ID_KEY to subscriptionReceiptData.productId,
@@ -26,7 +26,7 @@ class SubscriptionStageDataSource @Inject constructor(
             )
         )
 
-    fun getProductIdsList() = ((signUpDataSource.currentStage as? Stage.Other)
+    fun getProductIdsList() = ((uiaDataSource.currentStage as? Stage.Other)
         ?.params?.get(SUBSCRIPTION_IDS_PARAMS_KEY) as? List<*>)
         ?.map { it.toString() }
         ?: emptyList()
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageFragment.kt
similarity index 94%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageFragment.kt
index 6e723478ab4b6c616af2f1559e655f86bec18c3d..92f6fb7fadc52fbe4a01cb490cd8774015591648 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageFragment.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.subscription_stage
+package org.futo.circles.auth.feature.uia.stages.subscription_stage
 
 import android.os.Bundle
 import android.view.View
@@ -9,7 +9,7 @@ import com.google.android.material.divider.MaterialDividerItemDecoration
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentSubscriptionStageBinding
-import org.futo.circles.auth.feature.sign_up.subscription_stage.list.SubscriptionsAdapter
+import org.futo.circles.auth.feature.uia.stages.subscription_stage.list.SubscriptionsAdapter
 import org.futo.circles.auth.model.SubscriptionReceiptData
 import org.futo.circles.auth.subscriptions.ItemPurchasedListener
 import org.futo.circles.auth.subscriptions.SubscriptionProvider
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageViewModel.kt
similarity index 97%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageViewModel.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageViewModel.kt
index 65d58bc5e206c0c1510c65ae44b45ff15ea5526c..cd838a003c7bfac461526326f2aff1737c3b068d 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/SubscriptionStageViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/SubscriptionStageViewModel.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.subscription_stage
+package org.futo.circles.auth.feature.uia.stages.subscription_stage
 
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/list/SubscriptionViewHolder.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/list/SubscriptionViewHolder.kt
similarity index 92%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/list/SubscriptionViewHolder.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/list/SubscriptionViewHolder.kt
index b14da3fa1c70fea6658c18d8639899a4cd67bcbd..2e84a24dfaa9e3cff6bd58470ff3d378bfb124c6 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/list/SubscriptionViewHolder.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/list/SubscriptionViewHolder.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.subscription_stage.list
+package org.futo.circles.auth.feature.uia.stages.subscription_stage.list
 
 import android.view.ViewGroup
 import androidx.recyclerview.widget.RecyclerView
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/list/SubscriptionsAdapter.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/list/SubscriptionsAdapter.kt
similarity index 90%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/list/SubscriptionsAdapter.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/list/SubscriptionsAdapter.kt
index 4adda9540a9e86bb7446265ca4e85c302e416dff..d8160878b5069a9e37a1ee93ea77e8aa678e5db2 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/subscription_stage/list/SubscriptionsAdapter.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/subscription_stage/list/SubscriptionsAdapter.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.subscription_stage.list
+package org.futo.circles.auth.feature.uia.stages.subscription_stage.list
 
 import android.view.ViewGroup
 import org.futo.circles.core.base.list.BaseRvAdapter
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f67bb85d22a0c22f2676359347535ce239655c4c
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsDataSource.kt
@@ -0,0 +1,37 @@
+package org.futo.circles.auth.feature.uia.stages.terms
+
+import androidx.lifecycle.MutableLiveData
+import org.futo.circles.auth.extensions.toTermsListItems
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.auth.model.TermsListItem
+import org.futo.circles.core.extensions.Response
+import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
+import org.matrix.android.sdk.api.auth.registration.Stage
+import javax.inject.Inject
+
+class AcceptTermsDataSource @Inject constructor() {
+
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+    val termsListLiveData by lazy { MutableLiveData(getTermsList()) }
+
+    suspend fun acceptTerms(): Response<Unit> {
+        val result = uiaDataSource.performUIAStage(
+            mapOf(TYPE_PARAM_KEY to LoginFlowTypes.TERMS)
+        )
+        return when (result) {
+            is Response.Success -> Response.Success(Unit)
+            is Response.Error -> result
+        }
+    }
+
+    fun changeTermCheck(item: TermsListItem) {
+        termsListLiveData.value =
+            termsListLiveData.value?.map { if (it.id == item.id) it.copy(isChecked = !it.isChecked) else it }
+    }
+
+    private fun getTermsList() =
+        (uiaDataSource.currentStage as? Stage.Terms)?.policies?.toTermsListItems()
+            ?: emptyList()
+
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/AcceptTermsFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsFragment.kt
similarity index 95%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/AcceptTermsFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsFragment.kt
index ee5643d5c2e7e50a7965262832dfde828435fa10..8b0912dc799c37fdca32a3762bb3256a92403cdf 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/AcceptTermsFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsFragment.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.terms
+package org.futo.circles.auth.feature.uia.stages.terms
 
 import android.os.Bundle
 import android.view.View
@@ -10,7 +10,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentAcceptTermsBinding
 import org.futo.circles.auth.extensions.openCustomTabUrl
-import org.futo.circles.auth.feature.sign_up.terms.list.TermsListAdapter
+import org.futo.circles.auth.feature.uia.stages.terms.list.TermsListAdapter
 import org.futo.circles.auth.model.TermsListItem
 import org.futo.circles.core.base.fragment.HasLoadingState
 import org.futo.circles.core.base.fragment.ParentBackPressOwnerFragment
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/AcceptTermsViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsViewModel.kt
similarity index 65%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/AcceptTermsViewModel.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsViewModel.kt
index fea3071a07fbc71f9a95485a3d35f2dfe73123cb..aef81d8b116faa1c983d7cfc0a32a037d8f0f8b3 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/AcceptTermsViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/AcceptTermsViewModel.kt
@@ -1,23 +1,18 @@
-package org.futo.circles.auth.feature.sign_up.terms
+package org.futo.circles.auth.feature.uia.stages.terms
 
-import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
-import org.futo.circles.auth.base.BaseAcceptTermsDataSource
 import org.futo.circles.auth.model.TermsListItem
 import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
-import org.futo.circles.core.extensions.getOrThrow
 import org.futo.circles.core.extensions.launchBg
 import javax.inject.Inject
 
 @HiltViewModel
 class AcceptTermsViewModel @Inject constructor(
-    savedStateHandle: SavedStateHandle,
-    dataSourceFactory: BaseAcceptTermsDataSource.Factory
+    private val dataSource: AcceptTermsDataSource
 ) : ViewModel() {
 
-    private val dataSource = dataSourceFactory.create(savedStateHandle.getOrThrow("mode"))
 
     val acceptTermsLiveData = SingleEventLiveData<Response<Unit>>()
     val termsListLiveData = dataSource.termsListLiveData
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/list/TermsItemViewHolder.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/list/TermsItemViewHolder.kt
similarity index 94%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/list/TermsItemViewHolder.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/list/TermsItemViewHolder.kt
index 12a2d1082ce369e994737630fa0d6e5f30609bc8..1bf7ecbc75011eff6c445e31ed75b3e7ac6db3fa 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/list/TermsItemViewHolder.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/list/TermsItemViewHolder.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.terms.list
+package org.futo.circles.auth.feature.uia.stages.terms.list
 
 import android.view.ViewGroup
 import androidx.recyclerview.widget.RecyclerView
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/list/TermsListAdapter.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/list/TermsListAdapter.kt
similarity index 92%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/list/TermsListAdapter.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/list/TermsListAdapter.kt
index b63db703ab1633b080cb4157cb851cd9d5c176d3..f3065d4b84f43622bc10f192f52e44603854fb2b 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/terms/list/TermsListAdapter.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/terms/list/TermsListAdapter.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.terms.list
+package org.futo.circles.auth.feature.uia.stages.terms.list
 
 
 import android.view.ViewGroup
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9b21f76a52acd7eeb359239aded128462f67d764
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameDataSource.kt
@@ -0,0 +1,28 @@
+package org.futo.circles.auth.feature.uia.stages.username
+
+import androidx.lifecycle.MutableLiveData
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_USERNAME_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.core.extensions.Response
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import javax.inject.Inject
+
+class UsernameDataSource @Inject constructor() {
+
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+
+    val domainLiveData = MutableLiveData(uiaDataSource.domain)
+
+    suspend fun processUsernameStage(username: String): Response<RegistrationResult> =
+        uiaDataSource.performUIAStage(
+            mapOf(
+                TYPE_PARAM_KEY to ENROLL_USERNAME_TYPE,
+                USERNAME_PARAM_KEY to username
+            ), name = username
+        )
+
+    companion object {
+        private const val USERNAME_PARAM_KEY = "username"
+    }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameFragment.kt
similarity index 97%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameFragment.kt
index b1e07753e7d5fceec0714fb25eded0e9e6f15fd3..682e7238f9abd13092870a63ffb3982623a98560 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameFragment.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.username
+package org.futo.circles.auth.feature.uia.stages.username
 
 import android.os.Bundle
 import android.text.InputFilter
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameViewModel.kt
similarity index 93%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameViewModel.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameViewModel.kt
index 6a7ac038d09aa9c4a7da63f43f9be9138c2fa1d1..870e462fa34defdf4d1af09a5cfc55137b853034 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/username/UsernameViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/username/UsernameViewModel.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.username
+package org.futo.circles.auth.feature.uia.stages.username
 
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7a369e356ee345aaae7166976158ea5a772d9de8
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailDataSource.kt
@@ -0,0 +1,56 @@
+package org.futo.circles.auth.feature.uia.stages.validate_email
+
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_EMAIL_REQUEST_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.ENROLL_EMAIL_SUBMIT_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_EMAIL_REQUEST_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_EMAIL_SUBMIT_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.core.extensions.Response
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import org.matrix.android.sdk.api.auth.registration.Stage
+import javax.inject.Inject
+
+class ValidateEmailDataSource @Inject constructor() {
+
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+
+    suspend fun sendValidationCode(
+        email: String, subscribeToUpdates: Boolean
+    ): Response<RegistrationResult> = uiaDataSource.performUIAStage(
+        mapOf(
+            TYPE_PARAM_KEY to if (isLogin()) LOGIN_EMAIL_REQUEST_TOKEN_TYPE else ENROLL_EMAIL_REQUEST_TOKEN_TYPE,
+            EMAIL_PARAM_KEY to email,
+            SUBSCRIBE_TO_LIST to subscribeToUpdates
+        )
+    )
+
+    suspend fun validateEmail(code: String): Response<RegistrationResult> =
+        uiaDataSource.performUIAStage(
+            mapOf(
+                TYPE_PARAM_KEY to if (isLogin()) LOGIN_EMAIL_REQUEST_TOKEN_TYPE else ENROLL_EMAIL_SUBMIT_TOKEN_TYPE,
+                TOKEN_PARAM_KEY to code
+            )
+        )
+
+    private fun isLogin(): Boolean {
+        val currentStageKey = UIADataSourceProvider.getDataSourceOrThrow().getCurrentStageKey()
+        return currentStageKey == LOGIN_EMAIL_REQUEST_TOKEN_TYPE || currentStageKey == LOGIN_EMAIL_SUBMIT_TOKEN_TYPE
+    }
+
+    fun getPrefilledEmail(): String? =
+        if (isLogin()) ((uiaDataSource.currentStage as? Stage.Other)?.params?.get(EMAILS_LIST_KEY) as? List<*>)?.firstOrNull()
+            ?.toString() else null
+
+    fun shouldShowSubscribeToEmail(): Boolean =
+        (uiaDataSource.currentStage as? Stage.Other)?.params?.get(OFFER_LIST_SUBSCRIPTION_KEY) as? Boolean
+            ?: false
+
+    companion object {
+        private const val EMAIL_PARAM_KEY = "email"
+        private const val TOKEN_PARAM_KEY = "token"
+        private const val SUBSCRIBE_TO_LIST = "subscribe_to_list"
+        private const val OFFER_LIST_SUBSCRIPTION_KEY = "offer_list_subscription"
+        private const val EMAILS_LIST_KEY = "addresses"
+    }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailFragment.kt
similarity index 88%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailFragment.kt
index 93f52bb96cac30c7725189e9e1b786511603f7fe..96f7ff493e7894f65d4af37c27d6adf1ff95b364 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailFragment.kt
@@ -1,7 +1,8 @@
-package org.futo.circles.auth.feature.sign_up.validate_email
+package org.futo.circles.auth.feature.uia.stages.validate_email
 
 import android.os.Bundle
 import android.view.View
+import androidx.core.view.isVisible
 import androidx.core.widget.doAfterTextChanged
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
@@ -37,6 +38,7 @@ class ValidateEmailFragment : ParentBackPressOwnerFragment(R.layout.fragment_val
 
     private fun setupViews() {
         with(binding) {
+            setAlwaysDisabledViews(listOf(binding.etEmail))
             tilEmail.editText?.doAfterTextChanged {
                 it?.let { btnSendCode.isEnabled = it.isValidEmail() }
             }
@@ -57,7 +59,7 @@ class ValidateEmailFragment : ParentBackPressOwnerFragment(R.layout.fragment_val
             }
             btnSendCode.setOnClickListener {
                 startLoading(btnSendCode)
-                viewModel.sendCode(getEmailInput(), cbEmailUpdates.isChecked)
+                viewModel.sendCode(getEmailInput(), cbEmailUpdates.isChecked && cbEmailUpdates.isVisible)
             }
             btnValidate.setOnClickListener {
                 startLoading(btnValidate)
@@ -75,6 +77,14 @@ class ValidateEmailFragment : ParentBackPressOwnerFragment(R.layout.fragment_val
         viewModel.showSubscribeCheckLiveData.observeData(this) {
             binding.cbEmailUpdates.setIsVisible(it)
         }
+        viewModel.usersEmailLiveData.observeData(this) {email->
+            email?.let {
+                binding.etEmail.apply {
+                    setText(it)
+                    isEnabled = false
+                }
+            }
+        }
     }
 
     private fun getEmailInput(): String = binding.tilEmail.getText()
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailViewModel.kt
similarity index 89%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailViewModel.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailViewModel.kt
index dcd9b7d452c4f7d8284bcdc1bf2feef5fd128da8..b7a1b86fd90d372a973af0763c5707c0fc498476 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_email/ValidateEmailViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_email/ValidateEmailViewModel.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.validate_email
+package org.futo.circles.auth.feature.uia.stages.validate_email
 
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
@@ -18,6 +18,7 @@ class ValidateEmailViewModel @Inject constructor(
     val validateEmailLiveData = SingleEventLiveData<Response<RegistrationResult>>()
 
     val showSubscribeCheckLiveData = MutableLiveData(dataSource.shouldShowSubscribeToEmail())
+    val usersEmailLiveData = MutableLiveData(dataSource.getPrefilledEmail())
 
     fun sendCode(email: String, subscribeToUpdates: Boolean) {
         launchBg {
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a42a543df6b3769a3cefae83a89f9ba5d9bef4be
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenDataSource.kt
@@ -0,0 +1,25 @@
+package org.futo.circles.auth.feature.uia.stages.validate_token
+
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.LOGIN_REGISTRATION_TOKEN_TYPE
+import org.futo.circles.auth.feature.uia.UIADataSource.Companion.TYPE_PARAM_KEY
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
+import org.futo.circles.core.extensions.Response
+import org.matrix.android.sdk.api.auth.registration.RegistrationResult
+import javax.inject.Inject
+
+class ValidateTokenDataSource @Inject constructor() {
+
+    private val uiaDataSource = UIADataSourceProvider.getDataSourceOrThrow()
+
+    suspend fun validateToken(token: String): Response<RegistrationResult> =
+        uiaDataSource.performUIAStage(
+            mapOf(
+                TYPE_PARAM_KEY to LOGIN_REGISTRATION_TOKEN_TYPE,
+                TOKEN_PARAM_KEY to token
+            )
+        )
+
+    companion object {
+        private const val TOKEN_PARAM_KEY = "token"
+    }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenFragment.kt
similarity index 96%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenFragment.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenFragment.kt
index 32cec67c49c678e3696677eb7d0469a047565a16..a7131300009e60d58870219acd1b3c0a89c34d39 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenFragment.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.validate_token
+package org.futo.circles.auth.feature.uia.stages.validate_token
 
 import android.os.Bundle
 import android.view.View
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenViewModel.kt
similarity index 91%
rename from auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenViewModel.kt
rename to auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenViewModel.kt
index a17e35e81441ae7bcaa0a1380dd15b0ef83b81bd..05c34f9ebba287d1c949c2a3a00e62d343bb3d9e 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/validate_token/ValidateTokenViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/validate_token/ValidateTokenViewModel.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.auth.feature.sign_up.validate_token
+package org.futo.circles.auth.feature.uia.stages.validate_token
 
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
diff --git a/auth/src/main/java/org/futo/circles/auth/model/AuthUIAScreenNavigationEvent.kt b/auth/src/main/java/org/futo/circles/auth/model/AuthUIAScreenNavigationEvent.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c90c32c354d0559c78a7a71f8cdaa0340a595fe7
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/model/AuthUIAScreenNavigationEvent.kt
@@ -0,0 +1,7 @@
+package org.futo.circles.auth.model
+
+enum class AuthUIAScreenNavigationEvent {
+    Home,
+    ConfigureWorkspace,
+    PassPhrase
+}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/model/ConfirmationType.kt b/auth/src/main/java/org/futo/circles/auth/model/ConfirmationType.kt
index c1e5c6799db06ffbab997b9ce1f455e22e0a154b..ac7d8d417757e40b90ef292a5ba92ef4ecc4540b 100644
--- a/auth/src/main/java/org/futo/circles/auth/model/ConfirmationType.kt
+++ b/auth/src/main/java/org/futo/circles/auth/model/ConfirmationType.kt
@@ -24,11 +24,17 @@ data class LogOut(
 data class RemoveSession(
     override val titleRes: Int = R.string.remove_session,
     override val messageRes: Int = R.string.remove_session_message,
-    override val positiveButtonRes: Int =  org.futo.circles.core.R.string.remove
+    override val positiveButtonRes: Int = org.futo.circles.core.R.string.remove
 ) : ConfirmationType(titleRes, messageRes, positiveButtonRes)
 
 data class ResetKeys(
     override val titleRes: Int = R.string.reset_keys,
     override val messageRes: Int = R.string.reset_keys_message,
     override val positiveButtonRes: Int = R.string.confirm
+) : ConfirmationType(titleRes, messageRes, positiveButtonRes)
+
+data class ForgotPassword(
+    override val titleRes: Int = R.string.forgot_password,
+    override val messageRes: Int = R.string.forgot_password_message,
+    override val positiveButtonRes: Int = R.string.confirm
 ) : ConfirmationType(titleRes, messageRes, positiveButtonRes)
\ 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 13c641c2b3732d1709ddc70a30477d07e4f4e61b..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,6 +1,6 @@
 package org.futo.circles.auth.model
 
-import org.futo.circles.auth.base.BaseLoginStagesDataSource
+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
@@ -15,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 ?: ""
     }
 
 }
diff --git a/auth/src/main/java/org/futo/circles/auth/model/PasswordModeArg.kt b/auth/src/main/java/org/futo/circles/auth/model/PasswordModeArg.kt
deleted file mode 100644
index b3c401020eb142f1ecc30c04ec5f6862f108e33e..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/model/PasswordModeArg.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.futo.circles.auth.model
-
-enum class PasswordModeArg {
-    LoginPasswordStage,
-    LoginDirect,
-    LoginBsSpekeStage,
-    ReAuthPassword,
-    ReAuthBsSpekeLogin,
-    ReAuthBsSpekeSignup,
-    SignupPasswordStage,
-    SignupBsSpekeStage
-}
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/model/TermsModeArg.kt b/auth/src/main/java/org/futo/circles/auth/model/TermsModeArg.kt
deleted file mode 100644
index 40b08bb4f04871ee41d014f20b36c2410d300003..0000000000000000000000000000000000000000
--- a/auth/src/main/java/org/futo/circles/auth/model/TermsModeArg.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package org.futo.circles.auth.model
-
-
-enum class TermsModeArg { Login, Signup, ReAuth }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/model/UIAFlowType.kt b/auth/src/main/java/org/futo/circles/auth/model/UIAFlowType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0dcc9c4e3c62a64511420a37195aa5a0bdd78f2d
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/model/UIAFlowType.kt
@@ -0,0 +1,3 @@
+package org.futo.circles.auth.model
+
+enum class UIAFlowType { Login, Signup, ReAuth, ForgotPassword }
\ No newline at end of file
diff --git a/auth/src/main/java/org/futo/circles/auth/model/UIANavigationEvent.kt b/auth/src/main/java/org/futo/circles/auth/model/UIANavigationEvent.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e839433b95f10531f2736945cd9f4fc40b2e47af
--- /dev/null
+++ b/auth/src/main/java/org/futo/circles/auth/model/UIANavigationEvent.kt
@@ -0,0 +1,11 @@
+package org.futo.circles.auth.model
+
+
+enum class UIANavigationEvent {
+    TokenValidation,
+    Subscription,
+    AcceptTerm,
+    ValidateEmail,
+    Password,
+    Username
+}
\ No newline at end of file
diff --git a/auth/src/main/res/layout/fragment_login_stages.xml b/auth/src/main/res/layout/dialog_fragment_uia.xml
similarity index 97%
rename from auth/src/main/res/layout/fragment_login_stages.xml
rename to auth/src/main/res/layout/dialog_fragment_uia.xml
index dfdf2442a4bad9bd92f45501b6c7b906b126f4ac..e1fc82dc65361ace8efe120613a20af4bb6d511a 100644
--- a/auth/src/main/res/layout/fragment_login_stages.xml
+++ b/auth/src/main/res/layout/dialog_fragment_uia.xml
@@ -37,7 +37,7 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/toolbarDivider"
-        app:navGraph="@navigation/log_in_nav_graph" />
+        app:navGraph="@navigation/uia_nav_graph" />
 
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/auth/src/main/res/layout/fragment_log_in.xml b/auth/src/main/res/layout/fragment_log_in.xml
index d1dc63aea7da5c1b998bb4d4c0c4d71ecd5e9136..748cae09c33131182cf4a47b759afb6a998d44cb 100644
--- a/auth/src/main/res/layout/fragment_log_in.xml
+++ b/auth/src/main/res/layout/fragment_log_in.xml
@@ -142,6 +142,19 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/tilUserName" />
 
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/btnForgotPassword"
+            style="@style/Widget.Material3.Button.TextButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="8dp"
+            android:text="@string/forgot_password_question"
+            android:textColor="@color/blue"
+            android:textSize="17sp"
+            android:textStyle="bold"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/btnLogin" />
 
         <TextView
             android:id="@+id/tvResumeSession"
@@ -155,7 +168,7 @@
             app:layout_constraintBottom_toTopOf="@id/rvSwitchUsers"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/btnLogin"
+            app:layout_constraintTop_toBottomOf="@+id/btnForgotPassword"
             app:layout_constraintVertical_chainStyle="packed"
             tools:visibility="visible" />
 
diff --git a/auth/src/main/res/layout/fragment_select_sign_up_type.xml b/auth/src/main/res/layout/fragment_select_sign_up_type.xml
deleted file mode 100644
index c6e1fef7a6e78dd825d0682b64452a51c5f488f0..0000000000000000000000000000000000000000
--- a/auth/src/main/res/layout/fragment_select_sign_up_type.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout 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"
-    android:paddingHorizontal="24dp"
-    android:paddingVertical="8dp">
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/guidelineLogo"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.3" />
-
-    <ImageView
-        android:id="@+id/ivLogo"
-        style="@style/AuthLogoStyle"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constraintBottom_toTopOf="@id/guidelineLogo"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-
-    <TextView
-        android:id="@+id/tvServerTitle"
-        style="@style/headline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:text="@string/server"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/guidelineLogo" />
-
-    <RadioGroup
-        android:id="@+id/serverDomainGroup"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/tvServerTitle" />
-
-
-    <LinearLayout
-        android:id="@+id/lButtonsContainer"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="24dp"
-        android:orientation="vertical"
-        android:visibility="gone"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/serverDomainGroup"
-        tools:visibility="visible">
-
-        <org.futo.circles.core.view.LoadingButton
-            android:id="@+id/btnFree"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="16dp"
-            android:text="@string/sign_up_for_free" />
-
-        <TextView
-            android:id="@+id/tvOr"
-            style="@style/headline"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:gravity="center"
-            android:text="@string/or" />
-
-        <org.futo.circles.core.view.LoadingButton
-            android:id="@+id/btnSubscription"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:text="@string/sign_up_with_subscription" />
-
-    </LinearLayout>
-
-    <ProgressBar
-        android:id="@+id/flowProgress"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/serverDomainGroup"
-        tools:visibility="visible" />
-
-
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/auth/src/main/res/layout/fragment_sign_up.xml b/auth/src/main/res/layout/fragment_sign_up.xml
index 22f65cecca3bb69516579e638d699dc453ec4949..c6e1fef7a6e78dd825d0682b64452a51c5f488f0 100644
--- a/auth/src/main/res/layout/fragment_sign_up.xml
+++ b/auth/src/main/res/layout/fragment_sign_up.xml
@@ -1,42 +1,98 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout 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">
+    android:layout_height="match_parent"
+    android:paddingHorizontal="24dp"
+    android:paddingVertical="8dp">
 
-    <com.google.android.material.appbar.MaterialToolbar
-        android:id="@+id/toolbar"
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/guidelineLogo"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.3" />
+
+    <ImageView
+        android:id="@+id/ivLogo"
+        style="@style/AuthLogoStyle"
         android:layout_width="0dp"
-        android:layout_height="?attr/actionBarSize"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toTopOf="@id/guidelineLogo"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:navigationContentDescription="@string/back"
-        app:navigationIcon="?attr/homeAsUpIndicator"
-        app:subtitleCentered="true"
-        app:title="@string/sign_up"
-        app:titleCentered="true" />
-
-    <View
-        android:id="@+id/toolbarDivider"
-        android:layout_width="0dp"
-        android:layout_height="@dimen/divider_height"
-        android:background="@color/divider_color"
+        app:layout_constraintTop_toTopOf="parent" />
+
+
+    <TextView
+        android:id="@+id/tvServerTitle"
+        style="@style/headline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="@string/server"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/toolbar" />
+        app:layout_constraintTop_toBottomOf="@id/guidelineLogo" />
+
+    <RadioGroup
+        android:id="@+id/serverDomainGroup"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tvServerTitle" />
+
 
-    <androidx.fragment.app.FragmentContainerView
-        android:id="@+id/nav_host_fragment"
-        android:name="androidx.navigation.fragment.NavHostFragment"
+    <LinearLayout
+        android:id="@+id/lButtonsContainer"
         android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:defaultNavHost="true"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="24dp"
+        android:orientation="vertical"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/serverDomainGroup"
+        tools:visibility="visible">
+
+        <org.futo.circles.core.view.LoadingButton
+            android:id="@+id/btnFree"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dp"
+            android:text="@string/sign_up_for_free" />
+
+        <TextView
+            android:id="@+id/tvOr"
+            style="@style/headline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:gravity="center"
+            android:text="@string/or" />
+
+        <org.futo.circles.core.view.LoadingButton
+            android:id="@+id/btnSubscription"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:text="@string/sign_up_with_subscription" />
+
+    </LinearLayout>
+
+    <ProgressBar
+        android:id="@+id/flowProgress"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/toolbarDivider"
-        app:navGraph="@navigation/sign_up_nav_graph" />
+        app:layout_constraintTop_toBottomOf="@id/serverDomainGroup"
+        tools:visibility="visible" />
 
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/auth/src/main/res/navigation/log_in_nav_graph.xml b/auth/src/main/res/navigation/log_in_nav_graph.xml
deleted file mode 100644
index ecbed868195ba4ae37ba79e995078d3c0b27750b..0000000000000000000000000000000000000000
--- a/auth/src/main/res/navigation/log_in_nav_graph.xml
+++ /dev/null
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<navigation 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:id="@+id/log_in_nav_graph"
-    app:startDestination="@id/emptyFragment">
-
-    <action
-        android:id="@+id/to_acceptTerms"
-        app:destination="@id/acceptTermsFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="Login"
-            app:argType="org.futo.circles.auth.model.TermsModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_ReAuthAcceptTerms"
-        app:destination="@id/acceptTermsFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="ReAuth"
-            app:argType="org.futo.circles.auth.model.TermsModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_direct_login"
-        app:destination="@id/passwordFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="LoginDirect"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_password"
-        app:destination="@id/passwordFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="LoginPasswordStage"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_bsspeke"
-        app:destination="@id/passwordFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="LoginBsSpekeStage"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_reAuthPassword"
-        app:destination="@id/passwordFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="ReAuthPassword"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_reAuthBsSpekeLogin"
-        app:destination="@id/passwordFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="ReAuthBsSpekeLogin"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_reAuthBsSpekeSignup"
-        app:destination="@id/passwordFragmentLogin">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="ReAuthBsSpekeSignup"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <fragment
-        android:id="@+id/acceptTermsFragmentLogin"
-        android:name="org.futo.circles.auth.feature.sign_up.terms.AcceptTermsFragment"
-        tools:layout="@layout/fragment_accept_terms">
-
-        <argument
-            android:name="mode"
-            app:argType="org.futo.circles.auth.model.TermsModeArg"
-            app:nullable="false" />
-
-    </fragment>
-    <fragment
-        android:id="@+id/passwordFragmentLogin"
-        android:name="org.futo.circles.auth.feature.sign_up.password.PasswordFragment"
-        tools:layout="@layout/fragment_password">
-
-        <argument
-            android:name="mode"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </fragment>
-    <fragment
-        android:id="@+id/emptyFragment"
-        android:name="org.futo.circles.auth.feature.log_in.EmptyFragment"
-        tools:layout="@layout/fragment_empty" />
-</navigation>
\ No newline at end of file
diff --git a/auth/src/main/res/navigation/log_in_sessions_nav_graph.xml b/auth/src/main/res/navigation/log_in_sessions_nav_graph.xml
index bee1fe4e5e88206bda7b2f3e726a0d9123b055f4..fc797603864930459cf1eaa1763e3eeefb3fae7a 100644
--- a/auth/src/main/res/navigation/log_in_sessions_nav_graph.xml
+++ b/auth/src/main/res/navigation/log_in_sessions_nav_graph.xml
@@ -11,8 +11,8 @@
         android:label="ActiveSessionsDialogFragment"
         tools:layout="@layout/dialog_fragment_active_sessions">
         <action
-            android:id="@+id/to_reAuthStagesDialogFragment"
-            app:destination="@id/reAuthStagesDialogFragment" />
+            android:id="@+id/to_uiaDialogFragment"
+            app:destination="@id/UIADialogFragment" />
 
         <action
             android:id="@+id/to_verifySessionDialogFragment"
@@ -38,7 +38,7 @@
 
     </dialog>
     <dialog
-        android:id="@+id/reAuthStagesDialogFragment"
-        android:name="org.futo.circles.auth.feature.reauth.ReAuthStagesDialogFragment"
-        tools:layout="@layout/fragment_login_stages" />
+        android:id="@+id/UIADialogFragment"
+        android:name="org.futo.circles.auth.feature.uia.UIADialogFragment"
+        tools:layout="@layout/dialog_fragment_uia" />
 </navigation>
diff --git a/auth/src/main/res/navigation/nav_graph_auth.xml b/auth/src/main/res/navigation/nav_graph_auth.xml
index 70937bba43d1467a34a17b41f04db31465c5ffb6..f397b02cd2b5cf4678712103b7b63a55e53fcd2d 100644
--- a/auth/src/main/res/navigation/nav_graph_auth.xml
+++ b/auth/src/main/res/navigation/nav_graph_auth.xml
@@ -11,39 +11,35 @@
         android:label="Log In"
         tools:layout="@layout/fragment_log_in">
         <action
-            android:id="@+id/to_signUpFragment"
-            app:destination="@id/signUpFragment" />
-        <action
-            android:id="@+id/to_loginStagesFragment"
-            app:destination="@id/loginStagesFragment" />
+            android:id="@+id/to_uiaFragment"
+            app:destination="@id/uiaFragment" />
         <action
             android:id="@+id/to_homeFragment"
             app:destination="@id/homeFragment"
             app:popUpTo="@id/logInFragment"
             app:popUpToInclusive="true" />
+        <action
+            android:id="@+id/to_signUpFragment"
+            app:destination="@id/signUpFragment" />
     </fragment>
-    <fragment
-        android:id="@+id/loginStagesFragment"
-        android:name="org.futo.circles.auth.feature.log_in.stages.LogInStagesFragment"
-        tools:layout="@layout/fragment_login_stages">
+
+    <dialog
+        android:id="@+id/uiaFragment"
+        android:name="org.futo.circles.auth.feature.uia.UIADialogFragment"
+        tools:layout="@layout/dialog_fragment_uia">
 
         <action
             android:id="@+id/to_homeFragment"
             app:destination="@id/homeFragment"
             app:popUpTo="@id/logInFragment"
             app:popUpToInclusive="true" />
-    </fragment>
-    <fragment
-        android:id="@+id/signUpFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.SignUpFragment"
-        android:label="Sign up"
-        tools:layout="@layout/fragment_sign_up">
+
         <action
             android:id="@+id/to_ConfigureWorkspace"
             app:destination="@id/configureWorkspaceFragment"
             app:popUpTo="@id/logInFragment"
             app:popUpToInclusive="true" />
-    </fragment>
+    </dialog>
     <fragment
         android:id="@+id/setupProfileFragment"
         android:name="org.futo.circles.auth.feature.profile.setup.SetupProfileFragment"
@@ -66,4 +62,12 @@
             app:popUpTo="@id/configureWorkspaceFragment"
             app:popUpToInclusive="true" />
     </fragment>
+    <fragment
+        android:id="@+id/signUpFragment"
+        android:name="org.futo.circles.auth.feature.sign_up.SignUpFragment"
+        tools:layout="@layout/fragment_sign_up">
+        <action
+            android:id="@+id/to_uiaFragment"
+            app:destination="@id/uiaFragment" />
+    </fragment>
 </navigation>
diff --git a/auth/src/main/res/navigation/sign_up_nav_graph.xml b/auth/src/main/res/navigation/sign_up_nav_graph.xml
deleted file mode 100644
index 37e439db00db11b4550c4170fa5654a925ff2ea3..0000000000000000000000000000000000000000
--- a/auth/src/main/res/navigation/sign_up_nav_graph.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<navigation 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:id="@+id/sign_up_nav_graph"
-    app:startDestination="@id/selectSignUpTypeFragment">
-
-    <action
-        android:id="@+id/to_validateToken"
-        app:destination="@id/validateTokenFragment" />
-
-    <action
-        android:id="@+id/to_validateEmail"
-        app:destination="@id/validateEmailFragment" />
-
-    <action
-        android:id="@+id/to_acceptTerms"
-        app:destination="@id/acceptTermsFragment" />
-
-    <action
-        android:id="@+id/to_subscriptions"
-        app:destination="@id/subscriptionStageFragment" />
-
-    <action
-        android:id="@+id/to_username"
-        app:destination="@id/usernameFragment" />
-
-    <action
-        android:id="@+id/to_password"
-        app:destination="@id/passwordFragment">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="SignupPasswordStage"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <action
-        android:id="@+id/to_bsspeke"
-        app:destination="@id/passwordFragment">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="SignupBsSpekeStage"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </action>
-
-    <fragment
-        android:id="@+id/selectSignUpTypeFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.sign_up_type.SelectSignUpTypeFragment"
-        tools:layout="@layout/fragment_select_sign_up_type" />
-
-    <fragment
-        android:id="@+id/validateTokenFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.validate_token.ValidateTokenFragment"
-        tools:layout="@layout/fragment_validate_token" />
-
-    <fragment
-        android:id="@+id/validateEmailFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.validate_email.ValidateEmailFragment"
-        tools:layout="@layout/fragment_validate_email" />
-
-    <fragment
-        android:id="@+id/acceptTermsFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.terms.AcceptTermsFragment"
-        tools:layout="@layout/fragment_accept_terms">
-
-        <argument
-            android:name="mode"
-            android:defaultValue="Signup"
-            app:argType="org.futo.circles.auth.model.TermsModeArg"
-            app:nullable="false" />
-
-    </fragment>
-
-    <fragment
-        android:id="@+id/subscriptionStageFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.subscription_stage.SubscriptionStageFragment"
-        tools:layout="@layout/fragment_subscription_stage" />
-    <fragment
-        android:id="@+id/passwordFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.password.PasswordFragment"
-        tools:layout="@layout/fragment_password">
-
-        <argument
-            android:name="mode"
-            app:argType="org.futo.circles.auth.model.PasswordModeArg"
-            app:nullable="false" />
-
-    </fragment>
-
-    <fragment
-        android:id="@+id/usernameFragment"
-        android:name="org.futo.circles.auth.feature.sign_up.username.UsernameFragment"
-        tools:layout="@layout/fragment_username" />
-
-
-</navigation>
\ No newline at end of file
diff --git a/auth/src/main/res/navigation/uia_nav_graph.xml b/auth/src/main/res/navigation/uia_nav_graph.xml
new file mode 100644
index 0000000000000000000000000000000000000000..50842c7330d467183b74ecf29bca0b60a2bff7ce
--- /dev/null
+++ b/auth/src/main/res/navigation/uia_nav_graph.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<navigation 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:id="@+id/uia_nav_graph"
+    app:startDestination="@id/emptyFragment">
+
+    <action
+        android:id="@+id/to_validateToken"
+        app:destination="@id/validateTokenFragment" />
+
+    <action
+        android:id="@+id/to_validateEmail"
+        app:destination="@id/validateEmailFragment" />
+
+    <action
+        android:id="@+id/to_subscriptions"
+        app:destination="@id/subscriptionStageFragment" />
+
+    <action
+        android:id="@+id/to_username"
+        app:destination="@id/usernameFragment" />
+
+
+    <action
+        android:id="@+id/to_acceptTerms"
+        app:destination="@id/acceptTermsFragment" />
+
+    <action
+        android:id="@+id/to_password"
+        app:destination="@id/passwordFragment" />
+
+
+    <fragment
+        android:id="@+id/validateTokenFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.validate_token.ValidateTokenFragment"
+        tools:layout="@layout/fragment_validate_token" />
+
+    <fragment
+        android:id="@+id/validateEmailFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.validate_email.ValidateEmailFragment"
+        tools:layout="@layout/fragment_validate_email" />
+
+    <fragment
+        android:id="@+id/acceptTermsFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.terms.AcceptTermsFragment"
+        tools:layout="@layout/fragment_accept_terms" />
+
+    <fragment
+        android:id="@+id/subscriptionStageFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.subscription_stage.SubscriptionStageFragment"
+        tools:layout="@layout/fragment_subscription_stage" />
+    <fragment
+        android:id="@+id/passwordFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.password.PasswordFragment"
+        tools:layout="@layout/fragment_password" />
+
+    <fragment
+        android:id="@+id/usernameFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.username.UsernameFragment"
+        tools:layout="@layout/fragment_username" />
+
+    <fragment
+        android:id="@+id/emptyFragment"
+        android:name="org.futo.circles.auth.feature.uia.stages.EmptyFragment"
+        tools:layout="@layout/fragment_empty" />
+
+
+</navigation>
\ No newline at end of file
diff --git a/auth/src/main/res/values/strings.xml b/auth/src/main/res/values/strings.xml
index 946848f9ac26a139ce791252d89fe2969e21550e..a0368d8b476996342e9b164c2d473eccd944ca65 100644
--- a/auth/src/main/res/values/strings.xml
+++ b/auth/src/main/res/values/strings.xml
@@ -8,7 +8,7 @@
     <string name="initial_sync">Initial sync</string>
     <string name="not_found_login_flow_for_user">Log In flow for user not found</string>
     <string name="unsupported_login_method">Unsupported login method</string>
-    <string name="discard_current_login_progress">Discard current login progress?</string>
+    <string name="discard_current_auth_progress">Discard current auth progress?</string>
     <string name="set_password">Set password</string>
     <string name="log_in">Log In</string>
     <string name="wrong_signup_config">Wrong signup config!</string>
@@ -16,7 +16,6 @@
     <string name="eu_server_format"><![CDATA[<b>EU</b> - (%s)]]></string>
     <string name="sign_up_using_active_subscription">Sign Up using your active subscription</string>
     <string name="sign_up">Sign Up</string>
-    <string name="not_supported_stage_format">Not supported stage %s</string>
     <string name="sign_up_stage_subtitle_format">Stage %1$d of %2$d</string>
     <string name="no_backup_message">Keys backup not found</string>
     <string name="username_can_not_be_empty">Username can not be empty.</string>
@@ -49,7 +48,6 @@
     <string name="importing_keys">Importing keys.</string>
     <string name="decrypting_key">Decrypting key.</string>
     <string name="failed_to_get_restore_keys_version">Failed to get latest restore keys version.</string>
-    <string name="discard_current_registration_progress">Discard current registration progress?</string>
     <string name="domain">Domain</string>
     <string name="accept_terms_to_continue">Accept terms to continue</string>
     <string name="username">username</string>
@@ -106,6 +104,7 @@
     <string name="remove_session_message">In order to sign out from this session you should confirm your auth.</string>
     <string name="reset_keys">Reset keys</string>
     <string name="reset_keys_message">Confirm auth to reset keys and enable cross signing</string>
+    <string name="forgot_password_message">After verifying your email the new password and key backup will be set.\n\nWarning: With a new key backup you will not be able to see all your previous encrypted messages, this action can not be undone.</string>
     <string name="confirm">Confirm</string>
     <string name="failed_to_read_qr_code">Failed to read QR code</string>
     <string name="current_session">Current session</string>
@@ -176,6 +175,8 @@ Or, think about all of your friends across all of the places you\'ve ever lived.
     <string name="username_not_found">Username not found</string>
     <string name="invalid_validation_code">Invalid validation code</string>
     <string name="invalid_password">Invalid password</string>
+    <string name="forgot_password_question">Forgot password?</string>
+    <string name="forgot_password">Forgot password</string>
 
 
     <plurals name="days">
diff --git a/core/build.gradle b/core/build.gradle
index e315a488049d4d8a470e92d420de0d26260f1bf6..25be662b05a876935384d957d7f90ed66ef9629b 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -76,7 +76,7 @@ dependencies {
     kapt "com.google.dagger:hilt-compiler:$rootProject.ext.hilt_version"
 
     //Matrix
-    api('org.futo.gitlab.circles:matrix-android-sdk:v1.6.10.19@aar') {
+    api('org.futo.gitlab.circles:matrix-android-sdk:v1.6.10.21@aar') {
         transitive = true
     }