Login: check login flow - step 1

This commit is contained in:
Benoit Marty 2019-09-13 11:08:54 +02:00
parent a47a3ead1f
commit db8ea0f5e8
8 changed files with 99 additions and 18 deletions

View File

@ -21,12 +21,18 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
/** /**
* This interface defines methods to authenticate to a matrix server. * This interface defines methods to authenticate to a matrix server.
*/ */
interface Authenticator { interface Authenticator {
/**
* Request the supported login flows for this homeserver
*/
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResponse>): Cancelable
/** /**
* @param homeServerConnectionConfig this param is used to configure the Homeserver * @param homeServerConnectionConfig this param is used to configure the Homeserver
* @param login the login field * @param login the login field

View File

@ -17,10 +17,12 @@
package im.vector.matrix.android.internal.auth package im.vector.matrix.android.internal.auth
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
import im.vector.matrix.android.internal.network.NetworkConstants import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Headers import retrofit2.http.Headers
import retrofit2.http.POST import retrofit2.http.POST
@ -29,6 +31,13 @@ import retrofit2.http.POST
*/ */
internal interface AuthAPI { internal interface AuthAPI {
/**
* Get the supported login flow
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-login
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
fun getLoginFlows(): Call<LoginFlowResponse>
/** /**
* Pass params to the server for the current login phase. * Pass params to the server for the current login phase.
* Set all the timeouts to 1 minute * Set all the timeouts to 1 minute

View File

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
import im.vector.matrix.android.internal.auth.data.ThreePidMedium import im.vector.matrix.android.internal.auth.data.ThreePidMedium
import im.vector.matrix.android.internal.di.Unauthenticated import im.vector.matrix.android.internal.di.Unauthenticated
@ -62,11 +63,20 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
return sessionManager.getOrCreateSession(sessionParams) return sessionManager.getOrCreateSession(sessionParams)
} }
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResponse>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) {
val result = runCatching {
getLoginFlowInternal(homeServerConnectionConfig)
}
result.foldToCallback(callback)
}
return CancelableCoroutine(job)
}
override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,
login: String, login: String,
password: String, password: String,
callback: MatrixCallback<Session>): Cancelable { callback: MatrixCallback<Session>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) { val job = GlobalScope.launch(coroutineDispatchers.main) {
val sessionOrFailure = runCatching { val sessionOrFailure = runCatching {
authenticate(homeServerConnectionConfig, login, password) authenticate(homeServerConnectionConfig, login, password)
@ -74,7 +84,14 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
sessionOrFailure.foldToCallback(callback) sessionOrFailure.foldToCallback(callback)
} }
return CancelableCoroutine(job) return CancelableCoroutine(job)
}
private suspend fun getLoginFlowInternal(homeServerConnectionConfig: HomeServerConnectionConfig) = withContext(coroutineDispatchers.io) {
val authAPI = buildAuthAPI(homeServerConnectionConfig)
executeRequest<LoginFlowResponse> {
apiCall = authAPI.getLoginFlows()
}
} }
private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, private suspend fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig,

View File

@ -20,7 +20,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class LoginFlowResponse( data class LoginFlowResponse(
@Json(name = "flows") @Json(name = "flows")
val flows: List<InteractiveAuthenticationFlow> val flows: List<InteractiveAuthenticationFlow>
) )

View File

@ -35,7 +35,7 @@ internal object NetworkModule {
@Provides @Provides
@JvmStatic @JvmStatic
fun providesHttpLogingInterceptor(): HttpLoggingInterceptor { fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor {
val logger = FormattedJsonHttpLogger() val logger = FormattedJsonHttpLogger()
val interceptor = HttpLoggingInterceptor(logger) val interceptor = HttpLoggingInterceptor(logger)
interceptor.level = BuildConfig.OKHTTP_LOGGING_LEVEL interceptor.level = BuildConfig.OKHTTP_LOGGING_LEVEL

View File

@ -128,6 +128,28 @@ class LoginFragment : VectorBaseFragment() {
} }
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
when (state.asyncHomeServerLoginFlowRequest) {
is Loading -> {
progressBar.isVisible = true
touchArea.isVisible = true
passwordShown = false
renderPasswordField()
}
is Fail -> {
progressBar.isVisible = false
touchArea.isVisible = false
Toast.makeText(requireActivity(), "Authenticate failure: ${state.asyncHomeServerLoginFlowRequest.error}", Toast.LENGTH_LONG).show()
}
is Success -> {
progressBar.isVisible = false
touchArea.isVisible = false
// Check login flow
// TODO
}
}
when (state.asyncLoginAction) { when (state.asyncLoginAction) {
is Loading -> { is Loading -> {
progressBar.isVisible = true progressBar.isVisible = true

View File

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.extensions.configureAndStart import im.vector.riotx.core.extensions.configureAndStart
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
@ -104,6 +105,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
} }
private fun handleUpdateHomeserver(action: LoginActions.UpdateHomeServer) { private fun handleUpdateHomeserver(action: LoginActions.UpdateHomeServer) {
currentTask?.cancel()
Try { Try {
val homeServerUri = action.homeServerUrl val homeServerUri = action.homeServerUrl
homeServerConnectionConfig = HomeServerConnectionConfig.Builder() homeServerConnectionConfig = HomeServerConnectionConfig.Builder()
@ -111,11 +114,16 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
.build() .build()
} }
val homeServerConnectionConfigFinal = homeServerConnectionConfig
// TODO Do request if (homeServerConnectionConfigFinal == null) {
// This is invalid
/* setState {
currentTask?.cancel() copy(
asyncHomeServerLoginFlowRequest = Fail(Throwable("Baf format"))
)
}
} else {
setState { setState {
copy( copy(
@ -124,9 +132,31 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
} }
// TODO currentTask = currentTask = authenticator.getLoginFlow(homeServerConnectionConfigFinal, object : MatrixCallback<LoginFlowResponse> {
override fun onFailure(failure: Throwable) {
setState {
copy(
asyncHomeServerLoginFlowRequest = Fail(failure)
)
}
}
override fun onSuccess(data: LoginFlowResponse) {
setState {
copy(
asyncHomeServerLoginFlowRequest = Success(data)
)
}
handleLoginFlowResponse(data)
}
})
}
}
private fun handleLoginFlowResponse(loginFlowResponse: LoginFlowResponse) {
*/
} }

View File

@ -19,12 +19,9 @@ package im.vector.riotx.features.login
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
data class LoginViewState( data class LoginViewState(
val asyncLoginAction: Async<Unit> = Uninitialized, val asyncLoginAction: Async<Unit> = Uninitialized,
val asyncHomeServerLoginFlowRequest: Async<LoginFlowResult> = Uninitialized val asyncHomeServerLoginFlowRequest: Async<LoginFlowResponse> = Uninitialized
) : MvRxState ) : MvRxState
// TODO Remove
data class LoginFlowResult(val remover: Boolean)