diff --git a/changelog.d/5283.wip b/changelog.d/5283.wip
new file mode 100644
index 0000000000..1c2fbfcd61
--- /dev/null
+++ b/changelog.d/5283.wip
@@ -0,0 +1 @@
+FTUE - Adds the redesigned Sign In screen
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 00a073f832..aa4df5e308 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -60,6 +60,11 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.onboardingCombinedRegister,
                                 factory = VectorFeatures::isOnboardingCombinedRegisterEnabled
                         ),
+                        createBooleanFeature(
+                                label = "FTUE Combined login",
+                                key = DebugFeatureKeys.onboardingCombinedLogin,
+                                factory = VectorFeatures::isOnboardingCombinedLoginEnabled
+                        ),
                 )
         )
     }
diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index 1bc37ff97e..f36b1a804a 100644
--- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -57,6 +57,9 @@ class DebugVectorFeatures(
     override fun isOnboardingCombinedRegisterEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedRegister)
             ?: vectorFeatures.isOnboardingCombinedRegisterEnabled()
 
+    override fun isOnboardingCombinedLoginEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedLogin)
+            ?: vectorFeatures.isOnboardingCombinedLoginEnabled()
+
     override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
             ?: vectorFeatures.isScreenSharingEnabled()
 
@@ -113,6 +116,7 @@ object DebugFeatureKeys {
     val onboardingUseCase = booleanPreferencesKey("onboarding-splash-carousel")
     val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
     val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
+    val onboardingCombinedLogin = booleanPreferencesKey("onboarding-combined-login")
     val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
     val screenSharing = booleanPreferencesKey("screen-sharing")
 }
diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
index 3dba8b797b..e76f0ad672 100644
--- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt
@@ -101,6 +101,9 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment
 import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
 import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment
 import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseProfilePictureFragment
+import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedLoginFragment
+import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedRegisterFragment
+import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedServerSelectionFragment
 import im.vector.app.features.onboarding.ftueauth.FtueAuthEmailEntryFragment
 import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
 import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFragment
@@ -521,6 +524,21 @@ interface FragmentModule {
     @FragmentKey(FtueAuthPersonalizationCompleteFragment::class)
     fun bindFtueAuthPersonalizationCompleteFragment(fragment: FtueAuthPersonalizationCompleteFragment): Fragment
 
+    @Binds
+    @IntoMap
+    @FragmentKey(FtueAuthCombinedLoginFragment::class)
+    fun bindFtueAuthCombinedLoginFragment(fragment: FtueAuthCombinedLoginFragment): Fragment
+
+    @Binds
+    @IntoMap
+    @FragmentKey(FtueAuthCombinedRegisterFragment::class)
+    fun bindFtueAuthCombinedRegisterFragment(fragment: FtueAuthCombinedRegisterFragment): Fragment
+
+    @Binds
+    @IntoMap
+    @FragmentKey(FtueAuthCombinedServerSelectionFragment::class)
+    fun bindFtueAuthCombinedServerSelectionFragment(fragment: FtueAuthCombinedServerSelectionFragment): Fragment
+
     @Binds
     @IntoMap
     @FragmentKey(UserListFragment::class)
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index e3fded2824..6a7a0865de 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -26,6 +26,7 @@ interface VectorFeatures {
     fun isOnboardingUseCaseEnabled(): Boolean
     fun isOnboardingPersonalizeEnabled(): Boolean
     fun isOnboardingCombinedRegisterEnabled(): Boolean
+    fun isOnboardingCombinedLoginEnabled(): Boolean
     fun isScreenSharingEnabled(): Boolean
 
     enum class OnboardingVariant {
@@ -42,5 +43,6 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isOnboardingUseCaseEnabled() = true
     override fun isOnboardingPersonalizeEnabled() = false
     override fun isOnboardingCombinedRegisterEnabled() = false
+    override fun isOnboardingCombinedLoginEnabled() = false
     override fun isScreenSharingEnabled(): Boolean = true
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
index 68fc2d1c59..49fa815a56 100644
--- a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt
@@ -159,3 +159,9 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), resources.displayMetrics).toInt()
     }
 }
+
+fun SocialLoginButtonsView.render(ssoProviders: List<SsoIdentityProvider>?, mode: SocialLoginButtonsView.Mode, listener: (String?) -> Unit) {
+    this.mode = mode
+    this.ssoIdentityProviders = ssoProviders?.sorted()
+    this.listener = SocialLoginButtonsView.InteractionListener { listener(it) }
+}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt
index 3014b199b4..925c838d80 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/DirectLoginUseCase.kt
@@ -19,7 +19,7 @@ package im.vector.app.features.onboarding
 import im.vector.app.R
 import im.vector.app.core.extensions.andThen
 import im.vector.app.core.resources.StringProvider
-import im.vector.app.features.onboarding.OnboardingAction.LoginOrRegister
+import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction.LoginDirect
 import org.matrix.android.sdk.api.MatrixPatterns.getServerName
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -33,8 +33,8 @@ class DirectLoginUseCase @Inject constructor(
         private val uriFactory: UriFactory
 ) {
 
-    suspend fun execute(action: LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?): Result<Session> {
-        return fetchWellKnown(action.username, homeServerConnectionConfig)
+    suspend fun execute(action: LoginDirect, homeServerConnectionConfig: HomeServerConnectionConfig?): Result<Session> {
+        return fetchWellKnown(action.matrixId, homeServerConnectionConfig)
                 .andThen { wellKnown -> createSessionFor(wellKnown, action, homeServerConnectionConfig) }
     }
 
@@ -42,13 +42,13 @@ class DirectLoginUseCase @Inject constructor(
         authenticationService.getWellKnownData(matrixId, config)
     }
 
-    private suspend fun createSessionFor(data: WellknownResult, action: LoginOrRegister, config: HomeServerConnectionConfig?) = when (data) {
-        is WellknownResult.Prompt     -> loginDirect(action, data, config)
+    private suspend fun createSessionFor(data: WellknownResult, action: LoginDirect, config: HomeServerConnectionConfig?) = when (data) {
+        is WellknownResult.Prompt -> loginDirect(action, data, config)
         is WellknownResult.FailPrompt -> handleFailPrompt(data, action, config)
-        else                          -> onWellKnownError()
+        else -> onWellKnownError()
     }
 
-    private suspend fun handleFailPrompt(data: WellknownResult.FailPrompt, action: LoginOrRegister, config: HomeServerConnectionConfig?): Result<Session> {
+    private suspend fun handleFailPrompt(data: WellknownResult.FailPrompt, action: LoginDirect, config: HomeServerConnectionConfig?): Result<Session> {
         // Relax on IS discovery if homeserver is valid
         val isMissingInformationToLogin = data.homeServerUrl == null || data.wellKnown == null
         return when {
@@ -57,12 +57,12 @@ class DirectLoginUseCase @Inject constructor(
         }
     }
 
-    private suspend fun loginDirect(action: LoginOrRegister, wellKnownPrompt: WellknownResult.Prompt, config: HomeServerConnectionConfig?): Result<Session> {
+    private suspend fun loginDirect(action: LoginDirect, wellKnownPrompt: WellknownResult.Prompt, config: HomeServerConnectionConfig?): Result<Session> {
         val alteredHomeServerConnectionConfig = config?.updateWith(wellKnownPrompt) ?: fallbackConfig(action, wellKnownPrompt)
         return runCatching {
             authenticationService.directAuthentication(
                     alteredHomeServerConnectionConfig,
-                    action.username,
+                    action.matrixId,
                     action.password,
                     action.initialDeviceName
             )
@@ -74,8 +74,8 @@ class DirectLoginUseCase @Inject constructor(
             identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
     )
 
-    private fun fallbackConfig(action: LoginOrRegister, wellKnownPrompt: WellknownResult.Prompt) = HomeServerConnectionConfig(
-            homeServerUri = uriFactory.parse("https://${action.username.getServerName()}"),
+    private fun fallbackConfig(action: LoginDirect, wellKnownPrompt: WellknownResult.Prompt) = HomeServerConnectionConfig(
+            homeServerUri = uriFactory.parse("https://${action.matrixId.getServerName()}"),
             homeServerUriBase = uriFactory.parse(wellKnownPrompt.homeServerUrl),
             identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
     )
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
index 9f7dce56ea..bef624ddc4 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
@@ -46,9 +46,12 @@ sealed interface OnboardingAction : VectorViewModelAction {
     data class ResetPassword(val email: String, val newPassword: String) : OnboardingAction
     object ResetPasswordMailConfirmed : OnboardingAction
 
-    // Login or Register, depending on the signMode
-    data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
-    data class Register(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
+    sealed interface AuthenticateAction : OnboardingAction {
+        data class Register(val username: String, val password: String, val initialDeviceName: String) : AuthenticateAction
+        data class Login(val username: String, val password: String, val initialDeviceName: String) : AuthenticateAction
+        data class LoginDirect(val matrixId: String, val password: String, val initialDeviceName: String) : AuthenticateAction
+    }
+
     object StopEmailValidationCheck : OnboardingAction
 
     data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt
index 6ffece4ab6..5dbcd162f3 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewEvents.kt
@@ -37,6 +37,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {
     object OpenUseCaseSelection : OnboardingViewEvents()
     object OpenServerSelection : OnboardingViewEvents()
     object OpenCombinedRegister : OnboardingViewEvents()
+    object OpenCombinedLogin : OnboardingViewEvents()
     object EditServerSelection : OnboardingViewEvents()
     data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
     object OnLoginFlowRetrieved : OnboardingViewEvents()
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index cf730a0266..0bd61758bc 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -42,6 +42,7 @@ import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.ReAuthHelper
 import im.vector.app.features.login.ServerType
 import im.vector.app.features.login.SignMode
+import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
 import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
 import im.vector.app.features.onboarding.ftueauth.MatrixOrgRegistrationStagesComparator
 import kotlinx.coroutines.Job
@@ -139,8 +140,7 @@ class OnboardingViewModel @AssistedInject constructor(
             is OnboardingAction.UpdateSignMode             -> handleUpdateSignMode(action)
             is OnboardingAction.InitWith                   -> handleInitWith(action)
             is OnboardingAction.HomeServerChange           -> withAction(action) { handleHomeserverChange(action) }
-            is OnboardingAction.LoginOrRegister            -> handleLoginOrRegister(action).also { lastAction = action }
-            is OnboardingAction.Register                   -> handleRegisterWith(action).also { lastAction = action }
+            is AuthenticateAction                          -> withAction(action) { handleAuthenticateAction(action) }
             is OnboardingAction.LoginWithToken             -> handleLoginWithToken(action)
             is OnboardingAction.WebLoginSuccess            -> handleWebLoginSuccess(action)
             is OnboardingAction.ResetPassword              -> handleResetPassword(action)
@@ -165,6 +165,14 @@ class OnboardingViewModel @AssistedInject constructor(
         block(action)
     }
 
+    private fun handleAuthenticateAction(action: AuthenticateAction) {
+        when (action) {
+            is AuthenticateAction.Register    -> handleRegisterWith(action)
+            is AuthenticateAction.Login       -> handleLogin(action)
+            is AuthenticateAction.LoginDirect -> handleDirectLogin(action, homeServerConnectionConfig = null)
+        }
+    }
+
     private fun handleSplashAction(resetConfig: Boolean, onboardingFlow: OnboardingFlow) {
         if (resetConfig) {
             loginConfig = null
@@ -188,16 +196,21 @@ class OnboardingViewModel @AssistedInject constructor(
     }
 
     private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
-        val nextOnboardingStep = when (onboardingFlow) {
-            OnboardingFlow.SignUp       -> if (vectorFeatures.isOnboardingUseCaseEnabled()) {
-                OnboardingViewEvents.OpenUseCaseSelection
-            } else {
-                OnboardingViewEvents.OpenServerSelection
+        when (onboardingFlow) {
+            OnboardingFlow.SignUp       -> {
+                _viewEvents.post(
+                        if (vectorFeatures.isOnboardingUseCaseEnabled()) {
+                            OnboardingViewEvents.OpenUseCaseSelection
+                        } else {
+                            OnboardingViewEvents.OpenServerSelection
+                        }
+                )
             }
-            OnboardingFlow.SignIn,
-            OnboardingFlow.SignInSignUp -> OnboardingViewEvents.OpenServerSelection
+            OnboardingFlow.SignIn       -> if (vectorFeatures.isOnboardingCombinedLoginEnabled()) {
+                handle(OnboardingAction.HomeServerChange.SelectHomeServer(defaultHomeserverUrl))
+            } else _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
+            OnboardingFlow.SignInSignUp -> _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
         }
-        _viewEvents.post(nextOnboardingStep)
     }
 
     private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
@@ -209,7 +222,7 @@ class OnboardingViewModel @AssistedInject constructor(
                         ?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
                         ?.let { startAuthenticationFlow(finalLastAction, it) }
             }
-            is OnboardingAction.LoginOrRegister                   ->
+            is AuthenticateAction.LoginDirect                     ->
                 handleDirectLogin(
                         finalLastAction,
                         HomeServerConnectionConfig.Builder()
@@ -307,7 +320,7 @@ class OnboardingViewModel @AssistedInject constructor(
 
     private fun OnboardingViewState.hasSelectedMatrixOrg() = selectedHomeserver.userFacingUrl == matrixOrgUrl
 
-    private fun handleRegisterWith(action: OnboardingAction.Register) {
+    private fun handleRegisterWith(action: AuthenticateAction.Register) {
         reAuthHelper.data = action.password
         handleRegisterAction(
                 RegisterAction.CreateAccount(
@@ -482,16 +495,7 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
-    private fun handleLoginOrRegister(action: OnboardingAction.LoginOrRegister) = withState { state ->
-        when (state.signMode) {
-            SignMode.Unknown            -> error("Developer error, invalid sign mode")
-            SignMode.SignIn             -> handleLogin(action)
-            SignMode.SignUp             -> handleRegisterWith(OnboardingAction.Register(action.username, action.password, action.initialDeviceName))
-            SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
-        }
-    }
-
-    private fun handleDirectLogin(action: OnboardingAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
+    private fun handleDirectLogin(action: AuthenticateAction.LoginDirect, homeServerConnectionConfig: HomeServerConnectionConfig?) {
         setState { copy(isLoading = true) }
         currentJob = viewModelScope.launch {
             directLoginUseCase.execute(action, homeServerConnectionConfig).fold(
@@ -504,7 +508,7 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
-    private fun handleLogin(action: OnboardingAction.LoginOrRegister) {
+    private fun handleLogin(action: AuthenticateAction.Login) {
         val safeLoginWizard = loginWizard
 
         if (safeLoginWizard == null) {
@@ -648,7 +652,11 @@ class OnboardingViewModel @AssistedInject constructor(
         when (trigger) {
             is OnboardingAction.HomeServerChange.EditHomeServer   -> {
                 when (awaitState().onboardingFlow) {
-                    OnboardingFlow.SignUp -> internalRegisterAction(RegisterAction.StartRegistration) { _ ->
+                    OnboardingFlow.SignUp -> internalRegisterAction(RegisterAction.StartRegistration) {
+                        updateServerSelection(config, serverTypeOverride, authResult)
+                        _viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
+                    }
+                    OnboardingFlow.SignIn -> {
                         updateServerSelection(config, serverTypeOverride, authResult)
                         _viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
                     }
@@ -661,7 +669,10 @@ class OnboardingViewModel @AssistedInject constructor(
                     when (awaitState().onboardingFlow) {
                         OnboardingFlow.SignIn -> {
                             updateSignMode(SignMode.SignIn)
-                            _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignIn))
+                            when (vectorFeatures.isOnboardingCombinedLoginEnabled()) {
+                                true  -> _viewEvents.post(OnboardingViewEvents.OpenCombinedLogin)
+                                false -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignIn))
+                            }
                         }
                         OnboardingFlow.SignUp -> {
                             updateSignMode(SignMode.SignUp)
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
new file mode 100644
index 0000000000..7324c4fbb1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.onboarding.ftueauth
+
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.autofill.HintConstants
+import androidx.core.view.isVisible
+import androidx.lifecycle.lifecycleScope
+import im.vector.app.R
+import im.vector.app.core.extensions.content
+import im.vector.app.core.extensions.editText
+import im.vector.app.core.extensions.hideKeyboard
+import im.vector.app.core.extensions.hidePassword
+import im.vector.app.core.extensions.realignPercentagesToParent
+import im.vector.app.core.extensions.setOnImeDoneListener
+import im.vector.app.core.extensions.toReducedUrl
+import im.vector.app.databinding.FragmentFtueCombinedLoginBinding
+import im.vector.app.features.login.LoginMode
+import im.vector.app.features.login.SSORedirectRouterActivity
+import im.vector.app.features.login.SocialLoginButtonsView
+import im.vector.app.features.login.render
+import im.vector.app.features.onboarding.OnboardingAction
+import im.vector.app.features.onboarding.OnboardingViewEvents
+import im.vector.app.features.onboarding.OnboardingViewState
+import kotlinx.coroutines.flow.launchIn
+import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
+import javax.inject.Inject
+
+class FtueAuthCombinedLoginFragment @Inject constructor(
+        private val loginFieldsValidation: LoginFieldsValidation,
+        private val loginErrorParser: LoginErrorParser
+) : AbstractSSOFtueAuthFragment<FragmentFtueCombinedLoginBinding>() {
+
+    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueCombinedLoginBinding {
+        return FragmentFtueCombinedLoginBinding.inflate(inflater, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        setupSubmitButton()
+        views.loginRoot.realignPercentagesToParent()
+        views.editServerButton.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection)) }
+        views.loginPasswordInput.setOnImeDoneListener { submit() }
+    }
+
+    private fun setupSubmitButton() {
+        views.loginSubmit.setOnClickListener { submit() }
+        observeContentChangesAndResetErrors(views.loginInput, views.loginPasswordInput, views.loginSubmit)
+                .launchIn(viewLifecycleOwner.lifecycleScope)
+    }
+
+    private fun submit() {
+        cleanupUi()
+        loginFieldsValidation.validate(views.loginInput.content(), views.loginPasswordInput.content())
+                .onUsernameOrIdError { views.loginInput.error = it }
+                .onPasswordError { views.loginPasswordInput.error = it }
+                .onValid { usernameOrId, password ->
+                    val initialDeviceName = getString(R.string.login_default_session_public_name)
+                    viewModel.handle(OnboardingAction.AuthenticateAction.Login(usernameOrId, password, initialDeviceName))
+                }
+    }
+
+    private fun cleanupUi() {
+        views.loginSubmit.hideKeyboard()
+        views.loginInput.error = null
+        views.loginPasswordInput.error = null
+    }
+
+    override fun resetViewModel() {
+        viewModel.handle(OnboardingAction.ResetAuthenticationAttempt)
+    }
+
+    override fun onError(throwable: Throwable) {
+        // Trick to display the error without text.
+        views.loginInput.error = " "
+        loginErrorParser.parse(throwable, views.loginPasswordInput.content())
+                .onUnknown { super.onError(it) }
+                .onUsernameOrIdError { views.loginInput.error = it }
+                .onPasswordError { views.loginPasswordInput.error = it }
+    }
+
+    override fun updateWithState(state: OnboardingViewState) {
+        setupUi(state)
+        setupAutoFill()
+
+        views.selectedServerName.text = state.selectedHomeserver.userFacingUrl.toReducedUrl()
+        views.selectedServerDescription.text = state.selectedHomeserver.description
+
+        if (state.isLoading) {
+            // Ensure password is hidden
+            views.loginPasswordInput.editText().hidePassword()
+        }
+    }
+
+    private fun setupUi(state: OnboardingViewState) {
+        when (state.selectedHomeserver.preferredLoginMode) {
+            is LoginMode.SsoAndPassword -> {
+                showUsernamePassword()
+                renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
+            }
+            is LoginMode.Sso            -> {
+                hideUsernamePassword()
+                renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
+            }
+            else                        -> {
+                showUsernamePassword()
+                hideSsoProviders()
+            }
+        }
+    }
+
+    private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
+        views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
+        views.ssoButtonsHeader.isVisible = views.ssoGroup.isVisible && views.loginEntryGroup.isVisible
+        views.ssoButtons.render(ssoProviders, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
+            viewModel.getSsoUrl(
+                    redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
+                    deviceId = deviceId,
+                    providerId = id
+            )?.let { openInCustomTab(it) }
+        }
+    }
+
+    private fun hideSsoProviders() {
+        views.ssoGroup.isVisible = false
+        views.ssoButtons.ssoIdentityProviders = null
+    }
+
+    private fun hideUsernamePassword() {
+        views.loginEntryGroup.isVisible = false
+    }
+
+    private fun showUsernamePassword() {
+        views.loginEntryGroup.isVisible = true
+    }
+
+    private fun setupAutoFill() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            views.loginInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
+            views.loginPasswordInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
index 0755f18c8c..62aa0854c3 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt
@@ -21,7 +21,6 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
 import androidx.autofill.HintConstants
 import androidx.core.text.isDigitsOnly
 import androidx.core.view.isVisible
@@ -31,22 +30,22 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import im.vector.app.R
 import im.vector.app.core.extensions.content
 import im.vector.app.core.extensions.editText
-import im.vector.app.core.extensions.hasContentFlow
 import im.vector.app.core.extensions.hasSurroundingSpaces
 import im.vector.app.core.extensions.hideKeyboard
 import im.vector.app.core.extensions.hidePassword
 import im.vector.app.core.extensions.realignPercentagesToParent
+import im.vector.app.core.extensions.setOnImeDoneListener
 import im.vector.app.core.extensions.toReducedUrl
 import im.vector.app.databinding.FragmentFtueCombinedRegisterBinding
 import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.SocialLoginButtonsView
+import im.vector.app.features.login.render
 import im.vector.app.features.onboarding.OnboardingAction
+import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
 import im.vector.app.features.onboarding.OnboardingViewEvents
 import im.vector.app.features.onboarding.OnboardingViewState
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
 import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
 import org.matrix.android.sdk.api.failure.isInvalidPassword
 import org.matrix.android.sdk.api.failure.isInvalidUsername
@@ -66,36 +65,16 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
         super.onViewCreated(view, savedInstanceState)
         setupSubmitButton()
         views.createAccountRoot.realignPercentagesToParent()
-        views.editServerButton.debouncedClicks {
-            viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection))
-        }
-
-        views.createAccountPasswordInput.editText().setOnEditorActionListener { _, actionId, _ ->
-            if (actionId == EditorInfo.IME_ACTION_DONE) {
-                submit()
-                return@setOnEditorActionListener true
-            }
-            return@setOnEditorActionListener false
-        }
+        views.editServerButton.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection)) }
+        views.createAccountPasswordInput.setOnImeDoneListener { submit() }
     }
 
     private fun setupSubmitButton() {
         views.createAccountSubmit.setOnClickListener { submit() }
-        observeInputFields()
-                .onEach {
-                    views.createAccountPasswordInput.error = null
-                    views.createAccountInput.error = null
-                    views.createAccountSubmit.isEnabled = it
-                }
+        observeContentChangesAndResetErrors(views.createAccountInput, views.createAccountPasswordInput, views.createAccountSubmit)
                 .launchIn(viewLifecycleOwner.lifecycleScope)
     }
 
-    private fun observeInputFields() = combine(
-            views.createAccountInput.hasContentFlow { it.trim() },
-            views.createAccountPasswordInput.hasContentFlow(),
-            transform = { isLoginNotEmpty, isPasswordNotEmpty -> isLoginNotEmpty && isPasswordNotEmpty }
-    )
-
     private fun submit() {
         withState(viewModel) { state ->
             cleanupUi()
@@ -119,7 +98,7 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
             }
 
             if (error == 0) {
-                viewModel.handle(OnboardingAction.Register(login, password, getString(R.string.login_default_session_public_name)))
+                viewModel.handle(AuthenticateAction.Register(login, password, getString(R.string.login_default_session_public_name)))
             }
         }
     }
@@ -185,9 +164,7 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
 
     private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
         views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
-        views.ssoButtons.mode = SocialLoginButtonsView.Mode.MODE_CONTINUE
-        views.ssoButtons.ssoIdentityProviders = ssoProviders?.sorted()
-        views.ssoButtons.listener = SocialLoginButtonsView.InteractionListener { id ->
+        views.ssoButtons.render(ssoProviders, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
             viewModel.getSsoUrl(
                     redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
                     deviceId = deviceId,
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt
index 2308280400..98d9a24999 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt
@@ -26,6 +26,7 @@ import androidx.autofill.HintConstants
 import androidx.core.text.isDigitsOnly
 import androidx.core.view.isVisible
 import androidx.lifecycle.lifecycleScope
+import com.airbnb.mvrx.withState
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import im.vector.app.R
 import im.vector.app.core.extensions.hideKeyboard
@@ -119,40 +120,43 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
     }
 
     private fun submit() {
-        cleanupUi()
+        withState(viewModel) { state ->
+            cleanupUi()
 
-        val login = views.loginField.text.toString()
-        val password = views.passwordField.text.toString()
+            val login = views.loginField.text.toString()
+            val password = views.passwordField.text.toString()
 
-        // This can be called by the IME action, so deal with empty cases
-        var error = 0
-        if (login.isEmpty()) {
-            views.loginFieldTil.error = getString(
-                    if (isSignupMode) {
-                        R.string.error_empty_field_choose_user_name
-                    } else {
-                        R.string.error_empty_field_enter_user_name
-                    }
-            )
-            error++
-        }
-        if (isSignupMode && isNumericOnlyUserIdForbidden && login.isDigitsOnly()) {
-            views.loginFieldTil.error = getString(R.string.error_forbidden_digits_only_username)
-            error++
-        }
-        if (password.isEmpty()) {
-            views.passwordFieldTil.error = getString(
-                    if (isSignupMode) {
-                        R.string.error_empty_field_choose_password
-                    } else {
-                        R.string.error_empty_field_your_password
-                    }
-            )
-            error++
-        }
+            // This can be called by the IME action, so deal with empty cases
+            var error = 0
+            if (login.isEmpty()) {
+                views.loginFieldTil.error = getString(
+                        if (isSignupMode) {
+                            R.string.error_empty_field_choose_user_name
+                        } else {
+                            R.string.error_empty_field_enter_user_name
+                        }
+                )
+                error++
+            }
+            if (isSignupMode && isNumericOnlyUserIdForbidden && login.isDigitsOnly()) {
+                views.loginFieldTil.error = getString(R.string.error_forbidden_digits_only_username)
+                error++
+            }
+            if (password.isEmpty()) {
+                views.passwordFieldTil.error = getString(
+                        if (isSignupMode) {
+                            R.string.error_empty_field_choose_password
+                        } else {
+                            R.string.error_empty_field_your_password
+                        }
+                )
+                error++
+            }
 
-        if (error == 0) {
-            viewModel.handle(OnboardingAction.LoginOrRegister(login, password, getString(R.string.login_default_session_public_name)))
+            if (error == 0) {
+                val initialDeviceName = getString(R.string.login_default_session_public_name)
+                viewModel.handle(state.signMode.toAuthenticateAction(login, password, initialDeviceName))
+            }
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
index 8430b483d2..5ad6b7e78d 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
@@ -227,10 +227,15 @@ class FtueAuthVariant(
                         option = commonOption
                 )
             }
-            OnboardingViewEvents.OnHomeserverEdited                            -> activity.popBackstack()
+            OnboardingViewEvents.OnHomeserverEdited -> activity.popBackstack()
+            OnboardingViewEvents.OpenCombinedLogin  -> onStartCombinedLogin()
         }
     }
 
+    private fun onStartCombinedLogin() {
+        addRegistrationStageFragmentToBackstack(FtueAuthCombinedLoginFragment::class.java)
+    }
+
     private fun onRegistrationFlow(viewEvents: OnboardingViewEvents.RegistrationFlowResult) {
         when {
             registrationShouldFallback(viewEvents)               -> displayFallbackWebDialog()
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt
new file mode 100644
index 0000000000..8d63fbf547
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.onboarding.ftueauth
+
+import android.widget.Button
+import com.google.android.material.textfield.TextInputLayout
+import im.vector.app.core.extensions.hasContentFlow
+import im.vector.app.features.login.SignMode
+import im.vector.app.features.onboarding.OnboardingAction
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+
+fun SignMode.toAuthenticateAction(login: String, password: String, initialDeviceName: String): OnboardingAction.AuthenticateAction {
+    return when (this) {
+        SignMode.Unknown -> error("developer error")
+        SignMode.SignUp -> OnboardingAction.AuthenticateAction.Register(username = login, password, initialDeviceName)
+        SignMode.SignIn -> OnboardingAction.AuthenticateAction.Login(username = login, password, initialDeviceName)
+        SignMode.SignInWithMatrixId -> OnboardingAction.AuthenticateAction.LoginDirect(matrixId = login, password, initialDeviceName)
+    }
+}
+
+/**
+ * A flow to monitor content changes from both username/id and password fields,
+ * clearing errors and enabling/disabling the submission button on non empty content changes.
+ */
+fun observeContentChangesAndResetErrors(username: TextInputLayout, password: TextInputLayout, submit: Button): Flow<*> {
+    return combine(
+            username.hasContentFlow { it.trim() },
+            password.hasContentFlow(),
+            transform = { usernameHasContent, passwordHasContent -> usernameHasContent && passwordHasContent }
+    ).onEach {
+        username.error = null
+        password.error = null
+        submit.isEnabled = it
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginErrorParser.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginErrorParser.kt
new file mode 100644
index 0000000000..a92fdea04a
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginErrorParser.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.onboarding.ftueauth
+
+import im.vector.app.R
+import im.vector.app.core.error.ErrorFormatter
+import im.vector.app.core.resources.StringProvider
+import im.vector.app.features.onboarding.ftueauth.LoginErrorParser.LoginErrorResult
+import org.matrix.android.sdk.api.failure.isInvalidPassword
+import org.matrix.android.sdk.api.failure.isInvalidUsername
+import org.matrix.android.sdk.api.failure.isLoginEmailUnknown
+import javax.inject.Inject
+
+class LoginErrorParser @Inject constructor(
+        private val errorFormatter: ErrorFormatter,
+        private val stringProvider: StringProvider,
+) {
+    fun parse(throwable: Throwable, password: String): LoginErrorResult {
+        return when {
+            throwable.isInvalidUsername()                                    -> {
+                LoginErrorResult(throwable, usernameOrIdError = errorFormatter.toHumanReadable(throwable))
+            }
+            throwable.isLoginEmailUnknown()                                  -> {
+                LoginErrorResult(throwable, usernameOrIdError = stringProvider.getString(R.string.login_login_with_email_error))
+            }
+            throwable.isInvalidPassword() && password.hasSurroundingSpaces() -> {
+                LoginErrorResult(throwable, passwordError = stringProvider.getString(R.string.auth_invalid_login_param_space_in_password))
+            }
+            else                                                             -> {
+                LoginErrorResult(throwable)
+            }
+        }
+    }
+
+    private fun String.hasSurroundingSpaces() = trim() != this
+
+    data class LoginErrorResult(val cause: Throwable, val usernameOrIdError: String? = null, val passwordError: String? = null)
+}
+
+fun LoginErrorResult.onUnknown(action: (Throwable) -> Unit): LoginErrorResult {
+    when {
+        usernameOrIdError == null && passwordError == null -> action(cause)
+    }
+    return this
+}
+
+fun LoginErrorResult.onUsernameOrIdError(action: (String) -> Unit): LoginErrorResult {
+    usernameOrIdError?.let(action)
+    return this
+}
+
+fun LoginErrorResult.onPasswordError(action: (String) -> Unit): LoginErrorResult {
+    passwordError?.let(action)
+    return this
+}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginFieldsValidation.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginFieldsValidation.kt
new file mode 100644
index 0000000000..659a8cd2c1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginFieldsValidation.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.onboarding.ftueauth
+
+import im.vector.app.R
+import im.vector.app.core.resources.StringProvider
+import javax.inject.Inject
+
+class LoginFieldsValidation @Inject constructor(
+        private val stringProvider: StringProvider
+) {
+
+    fun validate(usernameOrId: String, password: String): LoginValidationResult {
+        return LoginValidationResult(usernameOrId, password, validateUsernameOrId(usernameOrId), validatePassword(password))
+    }
+
+    private fun validateUsernameOrId(usernameOrId: String): String? {
+        val accountError = when {
+            usernameOrId.isEmpty() -> stringProvider.getString(R.string.error_empty_field_enter_user_name)
+            else                   -> null
+        }
+        return accountError
+    }
+
+    private fun validatePassword(password: String): String? {
+        val passwordError = when {
+            password.isEmpty() -> stringProvider.getString(R.string.error_empty_field_your_password)
+            else               -> null
+        }
+        return passwordError
+    }
+}
+
+fun LoginValidationResult.onValid(action: (String, String) -> Unit): LoginValidationResult {
+    when {
+        usernameOrIdError == null && passwordError == null -> action(usernameOrId, password)
+    }
+    return this
+}
+
+fun LoginValidationResult.onUsernameOrIdError(action: (String) -> Unit): LoginValidationResult {
+    usernameOrIdError?.let(action)
+    return this
+}
+
+fun LoginValidationResult.onPasswordError(action: (String) -> Unit): LoginValidationResult {
+    passwordError?.let(action)
+    return this
+}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginValidationResult.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginValidationResult.kt
new file mode 100644
index 0000000000..caf127332a
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/LoginValidationResult.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.onboarding.ftueauth
+
+data class LoginValidationResult(
+        val usernameOrId: String,
+        val password: String,
+        val usernameOrIdError: String?,
+        val passwordError: String?
+)
diff --git a/vector/src/main/res/layout/fragment_ftue_combined_login.xml b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
new file mode 100644
index 0000000000..1b65056e9f
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.core.widget.NestedScrollView 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"
+    style="@style/LoginFormScrollView"
+    android:layout_height="match_parent"
+    android:background="?android:colorBackground"
+    android:fillViewport="true"
+    android:paddingTop="0dp"
+    android:paddingBottom="0dp">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/loginRoot"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/loginGutterStart"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_start_percent" />
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/loginGutterEnd"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="vertical"
+            app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
+
+        <Space
+            android:id="@+id/headerSpacing"
+            android:layout_width="match_parent"
+            android:layout_height="52dp"
+            app:layout_constraintBottom_toTopOf="@id/loginHeaderTitle"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0"
+            app:layout_constraintVertical_chainStyle="packed" />
+
+        <TextView
+            android:id="@+id/loginHeaderTitle"
+            style="@style/Widget.Vector.TextView.Title.Medium"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:gravity="center"
+            android:text="@string/ftue_auth_welcome_back_title"
+            android:textColor="?vctr_content_primary"
+            app:layout_constraintBottom_toTopOf="@id/titleContentSpacing"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/headerSpacing" />
+
+        <Space
+            android:id="@+id/titleContentSpacing"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toTopOf="@id/chooseYourServerHeader"
+            app:layout_constraintHeight_percent="0.03"
+            app:layout_constraintTop_toBottomOf="@id/loginHeaderTitle" />
+
+        <TextView
+            android:id="@+id/chooseYourServerHeader"
+            style="@style/Widget.Vector.TextView.Caption"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            android:layout_marginTop="4dp"
+            android:text="@string/ftue_auth_create_account_choose_server_header"
+            android:textColor="?vctr_content_secondary"
+            app:layout_constraintBottom_toTopOf="@id/selectedServerName"
+            app:layout_constraintEnd_toStartOf="@id/editServerButton"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/titleContentSpacing" />
+
+        <TextView
+            android:id="@+id/selectedServerName"
+            style="@style/Widget.Vector.TextView.Subtitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            android:textColor="?vctr_content_primary"
+            app:layout_constraintBottom_toTopOf="@id/selectedServerDescription"
+            app:layout_constraintEnd_toStartOf="@id/editServerButton"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader" />
+
+        <TextView
+            android:id="@+id/selectedServerDescription"
+            style="@style/Widget.Vector.TextView.Micro"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="12dp"
+            android:textColor="?vctr_content_tertiary"
+            app:layout_constraintBottom_toTopOf="@id/serverSelectionSpacing"
+            app:layout_constraintEnd_toStartOf="@id/editServerButton"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/selectedServerName" />
+
+        <Button
+            android:id="@+id/editServerButton"
+            style="@style/Widget.Vector.Button.Outlined"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minWidth="0dp"
+            android:paddingStart="12dp"
+            android:paddingEnd="12dp"
+            android:text="@string/ftue_auth_create_account_edit_server_selection"
+            android:textAllCaps="true"
+            app:layout_constraintBottom_toBottomOf="@id/selectedServerDescription"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintTop_toTopOf="@id/chooseYourServerHeader" />
+
+        <Space
+            android:id="@+id/serverSelectionSpacing"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toTopOf="@id/loginInput"
+            app:layout_constraintHeight_percent="0.05"
+            app:layout_constraintTop_toBottomOf="@id/selectedServerDescription" />
+
+        <View
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:background="?vctr_content_quaternary"
+            app:layout_constraintBottom_toBottomOf="@id/serverSelectionSpacing"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toTopOf="@id/serverSelectionSpacing" />
+
+        <androidx.constraintlayout.widget.Group
+            android:id="@+id/loginEntryGroup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="visible"
+            app:constraint_referenced_ids="loginInput,loginPasswordInput,entrySpacing,actionSpacing,loginSubmit" />
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/loginInput"
+            style="@style/Widget.Vector.TextInputLayout.Username"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:hint="@string/username"
+            app:layout_constraintBottom_toTopOf="@id/entrySpacing"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/serverSelectionSpacing">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:imeOptions="actionNext"
+                android:inputType="text"
+                android:maxLines="1"
+                android:nextFocusForward="@id/loginPasswordInput" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <Space
+            android:id="@+id/entrySpacing"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toTopOf="@id/loginPasswordInput"
+            app:layout_constraintHeight_percent="0.03"
+            app:layout_constraintTop_toBottomOf="@id/loginInput" />
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/loginPasswordInput"
+            style="@style/Widget.Vector.TextInputLayout.Password"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:hint="@string/login_signup_password_hint"
+            app:layout_constraintBottom_toTopOf="@id/actionSpacing"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/entrySpacing">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:imeOptions="actionDone"
+                android:inputType="textPassword"
+                android:maxLines="1" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <Space
+            android:id="@+id/actionSpacing"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toTopOf="@id/loginSubmit"
+            app:layout_constraintHeight_percent="0.02"
+            app:layout_constraintTop_toBottomOf="@id/loginPasswordInput" />
+
+        <Button
+            android:id="@+id/loginSubmit"
+            style="@style/Widget.Vector.Button.Login"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:text="@string/login_signup_submit"
+            android:textAllCaps="true"
+            app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/actionSpacing" />
+
+        <androidx.constraintlayout.widget.Group
+            android:id="@+id/ssoGroup"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            app:constraint_referenced_ids="ssoButtonsHeader,ssoButtons"
+            app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
+            app:layout_constraintTop_toBottomOf="@id/loginSubmit"
+            tools:visibility="visible" />
+
+        <TextView
+            android:id="@+id/ssoButtonsHeader"
+            style="@style/Widget.Vector.TextView.Subtitle.Medium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:text="@string/ftue_auth_create_account_sso_section_header"
+            android:textColor="?vctr_content_secondary"
+            app:layout_constraintBottom_toTopOf="@id/ssoButtons"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/loginSubmit" />
+
+        <im.vector.app.features.login.SocialLoginButtonsView
+            android:id="@+id/ssoButtons"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader"
+            tools:signMode="signup" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.core.widget.NestedScrollView>
diff --git a/vector/src/main/res/values/donottranslate.xml b/vector/src/main/res/values/donottranslate.xml
index 2dcd36d7c8..eb8efa0ad2 100755
--- a/vector/src/main/res/values/donottranslate.xml
+++ b/vector/src/main/res/values/donottranslate.xml
@@ -19,6 +19,8 @@
     <string name="ftue_auth_create_account_matrix_dot_org_server_description">Join millions for free on the largest public server</string>
     <string name="ftue_auth_create_account_edit_server_selection">Edit</string>
 
+    <string name="ftue_auth_welcome_back_title">Welcome back!</string>
+
     <string name="ftue_auth_choose_server_title">Choose your server</string>
     <string name="ftue_auth_choose_server_subtitle">What is the address of your server? Server is like a home for all your data.</string>
     <string name="ftue_auth_choose_server_entry_hint">Server URL</string>
diff --git a/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt
index 1fed03b601..d3600940ab 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/DirectLoginUseCaseTest.kt
@@ -32,13 +32,13 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.data.WellKnown
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
 
-private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
+private val A_DIRECT_LOGIN_ACTION = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
 private val A_WELLKNOWN_SUCCESS_RESULT = WellknownResult.Prompt("https://homeserverurl.com", identityServerUrl = null, WellKnown())
 private val A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT = WellknownResult.FailPrompt("https://homeserverurl.com", WellKnown())
 private val A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT = WellknownResult.FailPrompt(null, null)
 private val NO_HOMESERVER_CONFIG: HomeServerConnectionConfig? = null
 private val A_FALLBACK_CONFIG: HomeServerConnectionConfig = HomeServerConnectionConfig(
-        homeServerUri = FakeUri("https://${A_LOGIN_OR_REGISTER_ACTION.username.getServerName()}").instance,
+        homeServerUri = FakeUri("https://${A_DIRECT_LOGIN_ACTION.matrixId.getServerName()}").instance,
         homeServerUriBase = FakeUri(A_WELLKNOWN_SUCCESS_RESULT.homeServerUrl).instance,
         identityServerUri = null
 )
@@ -54,11 +54,11 @@ class DirectLoginUseCaseTest {
 
     @Test
     fun `when logging in directly, then returns success with direct session result`() = runTest {
-        fakeAuthenticationService.givenWellKnown(A_LOGIN_OR_REGISTER_ACTION.username, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
-        val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
+        fakeAuthenticationService.givenWellKnown(A_DIRECT_LOGIN_ACTION.matrixId, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
+        val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
         fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
 
-        val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
+        val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo Result.success(fakeSession)
     }
@@ -66,14 +66,14 @@ class DirectLoginUseCaseTest {
     @Test
     fun `given wellknown fails with content, when logging in directly, then returns success with direct session result`() = runTest {
         fakeAuthenticationService.givenWellKnown(
-                A_LOGIN_OR_REGISTER_ACTION.username,
+                A_DIRECT_LOGIN_ACTION.matrixId,
                 config = NO_HOMESERVER_CONFIG,
                 result = A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT
         )
-        val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
+        val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
         fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
 
-        val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
+        val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo Result.success(fakeSession)
     }
@@ -81,14 +81,14 @@ class DirectLoginUseCaseTest {
     @Test
     fun `given wellknown fails without content, when logging in directly, then returns well known error`() = runTest {
         fakeAuthenticationService.givenWellKnown(
-                A_LOGIN_OR_REGISTER_ACTION.username,
+                A_DIRECT_LOGIN_ACTION.matrixId,
                 config = NO_HOMESERVER_CONFIG,
                 result = A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT
         )
-        val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
+        val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
         fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
 
-        val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
+        val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
 
         result should { this.isFailure }
         result should { this.exceptionOrNull() is Exception }
@@ -97,20 +97,20 @@ class DirectLoginUseCaseTest {
 
     @Test
     fun `given wellknown throws, when logging in directly, then returns failure result with original cause`() = runTest {
-        fakeAuthenticationService.givenWellKnownThrows(A_LOGIN_OR_REGISTER_ACTION.username, config = NO_HOMESERVER_CONFIG, cause = AN_ERROR)
+        fakeAuthenticationService.givenWellKnownThrows(A_DIRECT_LOGIN_ACTION.matrixId, config = NO_HOMESERVER_CONFIG, cause = AN_ERROR)
 
-        val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
+        val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo Result.failure(AN_ERROR)
     }
 
     @Test
     fun `given direct authentication throws, when logging in directly, then returns failure result with original cause`() = runTest {
-        fakeAuthenticationService.givenWellKnown(A_LOGIN_OR_REGISTER_ACTION.username, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
-        val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
+        fakeAuthenticationService.givenWellKnown(A_DIRECT_LOGIN_ACTION.matrixId, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
+        val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
         fakeAuthenticationService.givenDirectAuthenticationThrows(A_FALLBACK_CONFIG, username, password, initialDeviceName, cause = AN_ERROR)
 
-        val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
+        val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
 
         result shouldBeEqualTo Result.failure(AN_ERROR)
     }
diff --git a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
index ac8a4c364e..e4e687536c 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
@@ -59,7 +59,7 @@ private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.SendAgainThreePid
 private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
 private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
 private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.NextStep(AN_IGNORED_FLOW_RESULT)
-private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
+private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
 private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
 private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
 private val SELECTED_HOMESERVER_STATE = SelectedHomeserverState(preferredLoginMode = LoginMode.Password)
@@ -142,11 +142,11 @@ class OnboardingViewModelTest {
     @Test
     fun `given has sign in with matrix id sign mode, when handling login or register action, then logs in directly`() = runTest {
         viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
-        fakeDirectLoginUseCase.givenSuccessResult(A_LOGIN_OR_REGISTER_ACTION, config = null, result = fakeSession)
+        fakeDirectLoginUseCase.givenSuccessResult(A_DIRECT_LOGIN, config = null, result = fakeSession)
         givenInitialisesSession(fakeSession)
         val test = viewModel.test()
 
-        viewModel.handle(A_LOGIN_OR_REGISTER_ACTION)
+        viewModel.handle(A_DIRECT_LOGIN)
 
         test
                 .assertStatesChanges(
@@ -161,11 +161,11 @@ class OnboardingViewModelTest {
     @Test
     fun `given has sign in with matrix id sign mode, when handling login or register action fails, then emits error`() = runTest {
         viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
-        fakeDirectLoginUseCase.givenFailureResult(A_LOGIN_OR_REGISTER_ACTION, config = null, cause = AN_ERROR)
+        fakeDirectLoginUseCase.givenFailureResult(A_DIRECT_LOGIN, config = null, cause = AN_ERROR)
         givenInitialisesSession(fakeSession)
         val test = viewModel.test()
 
-        viewModel.handle(A_LOGIN_OR_REGISTER_ACTION)
+        viewModel.handle(A_DIRECT_LOGIN)
 
         test
                 .assertStatesChanges(
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeDirectLoginUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeDirectLoginUseCase.kt
index 8a5c6b1cee..289c0a6159 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeDirectLoginUseCase.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeDirectLoginUseCase.kt
@@ -17,7 +17,7 @@
 package im.vector.app.test.fakes
 
 import im.vector.app.features.onboarding.DirectLoginUseCase
-import im.vector.app.features.onboarding.OnboardingAction
+import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
 import io.mockk.coEvery
 import io.mockk.mockk
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -25,11 +25,11 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 class FakeDirectLoginUseCase {
     val instance = mockk<DirectLoginUseCase>()
 
-    fun givenSuccessResult(action: OnboardingAction.LoginOrRegister, config: HomeServerConnectionConfig?, result: FakeSession) {
+    fun givenSuccessResult(action: AuthenticateAction.LoginDirect, config: HomeServerConnectionConfig?, result: FakeSession) {
         coEvery { instance.execute(action, config) } returns Result.success(result)
     }
 
-    fun givenFailureResult(action: OnboardingAction.LoginOrRegister, config: HomeServerConnectionConfig?, cause: Throwable) {
+    fun givenFailureResult(action: AuthenticateAction.LoginDirect, config: HomeServerConnectionConfig?, cause: Throwable) {
         coEvery { instance.execute(action, config) } returns Result.failure(cause)
     }
 }