Login screens: persist all data during login or registration
This commit is contained in:
parent
0c4e0890b1
commit
2e3763e8b4
@ -38,14 +38,19 @@ interface AuthenticationService {
|
||||
fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable
|
||||
|
||||
/**
|
||||
* Return a LoginWizard, to login to the homeserver
|
||||
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
|
||||
*/
|
||||
fun createLoginWizard(): LoginWizard
|
||||
fun getLoginWizard(): LoginWizard
|
||||
|
||||
/**
|
||||
* Return a RegistrationWizard, to create an matrix account on the homeserver
|
||||
* Return a RegistrationWizard, to create an matrix account on the homeserver. The login flow has to be retrieved first.
|
||||
*/
|
||||
fun getOrCreateRegistrationWizard(): RegistrationWizard
|
||||
fun getRegistrationWizard(): RegistrationWizard
|
||||
|
||||
/**
|
||||
* Cancel pending login or pending registration
|
||||
*/
|
||||
fun cancelPendingLoginOrRegistration()
|
||||
|
||||
/**
|
||||
* Check if there is an authenticated [Session].
|
||||
|
@ -22,6 +22,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.auth.AuthenticationService
|
||||
import im.vector.matrix.android.internal.auth.db.AuthRealmModule
|
||||
import im.vector.matrix.android.internal.auth.db.RealmPendingSessionStore
|
||||
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
|
||||
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||
import im.vector.matrix.android.internal.di.AuthDatabase
|
||||
@ -50,6 +51,7 @@ internal abstract class AuthModule {
|
||||
}
|
||||
.name("matrix-sdk-auth.realm")
|
||||
.modules(AuthRealmModule())
|
||||
// TODO Migration !!!! (test it)
|
||||
.deleteRealmIfMigrationNeeded()
|
||||
.build()
|
||||
}
|
||||
@ -58,6 +60,9 @@ internal abstract class AuthModule {
|
||||
@Binds
|
||||
abstract fun bindSessionParamsStore(sessionParamsStore: RealmSessionParamsStore): SessionParamsStore
|
||||
|
||||
@Binds
|
||||
abstract fun bindPendingSessionStore(pendingSessionStore: RealmPendingSessionStore): PendingSessionStore
|
||||
|
||||
@Binds
|
||||
abstract fun bindAuthenticationService(authenticationService: DefaultAuthenticationService): AuthenticationService
|
||||
|
||||
|
@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.SessionManager
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||
import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
||||
import im.vector.matrix.android.internal.auth.login.DefaultLoginWizard
|
||||
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationWizard
|
||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
||||
@ -46,10 +47,14 @@ internal class DefaultAuthenticationService @Inject constructor(@Unauthenticated
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionParamsStore: SessionParamsStore,
|
||||
private val sessionManager: SessionManager,
|
||||
private val sessionCreator: SessionCreator
|
||||
private val sessionCreator: SessionCreator,
|
||||
private val pendingSessionStore: PendingSessionStore
|
||||
) : AuthenticationService {
|
||||
|
||||
private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
|
||||
private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
|
||||
|
||||
private var currentLoginWizard: LoginWizard? = null
|
||||
private var currentRegistrationWizard: RegistrationWizard? = null
|
||||
|
||||
override fun hasAuthenticatedSessions(): Boolean {
|
||||
return sessionParamsStore.getLast() != null
|
||||
@ -67,9 +72,9 @@ internal class DefaultAuthenticationService @Inject constructor(@Unauthenticated
|
||||
}
|
||||
|
||||
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
|
||||
currentHomeServerConnectionConfig = null
|
||||
|
||||
return GlobalScope.launch(coroutineDispatchers.main) {
|
||||
pendingSessionStore.delete()
|
||||
|
||||
val result = runCatching {
|
||||
getLoginFlowInternal(homeServerConnectionConfig)
|
||||
}
|
||||
@ -77,7 +82,8 @@ internal class DefaultAuthenticationService @Inject constructor(@Unauthenticated
|
||||
{
|
||||
if (it is LoginFlowResult.Success) {
|
||||
// The homeserver exists and up to date, keep the config
|
||||
currentHomeServerConnectionConfig = homeServerConnectionConfig
|
||||
pendingSessionData = PendingSessionData(homeServerConnectionConfig)
|
||||
.also { data -> pendingSessionStore.savePendingSessionData(data) }
|
||||
}
|
||||
callback.onSuccess(it)
|
||||
},
|
||||
@ -109,29 +115,55 @@ internal class DefaultAuthenticationService @Inject constructor(@Unauthenticated
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOrCreateRegistrationWizard(): RegistrationWizard {
|
||||
currentHomeServerConnectionConfig?.let {
|
||||
// TODO Persist the wizard?
|
||||
return DefaultRegistrationWizard(
|
||||
it,
|
||||
okHttpClient,
|
||||
retrofitFactory,
|
||||
coroutineDispatchers,
|
||||
sessionCreator
|
||||
)
|
||||
} ?: error("Please call getLoginFlow() with success first")
|
||||
override fun getRegistrationWizard(): RegistrationWizard {
|
||||
return currentRegistrationWizard
|
||||
?: let {
|
||||
pendingSessionData?.homeServerConnectionConfig?.let {
|
||||
DefaultRegistrationWizard(
|
||||
okHttpClient,
|
||||
retrofitFactory,
|
||||
coroutineDispatchers,
|
||||
sessionCreator,
|
||||
pendingSessionStore
|
||||
).also {
|
||||
currentRegistrationWizard = it
|
||||
}
|
||||
} ?: error("Please call getLoginFlow() with success first")
|
||||
}
|
||||
}
|
||||
|
||||
override fun createLoginWizard(): LoginWizard {
|
||||
currentHomeServerConnectionConfig?.let {
|
||||
return DefaultLoginWizard(
|
||||
it,
|
||||
okHttpClient,
|
||||
retrofitFactory,
|
||||
coroutineDispatchers,
|
||||
sessionCreator
|
||||
)
|
||||
} ?: error("Please call getLoginFlow() with success first")
|
||||
override fun getLoginWizard(): LoginWizard {
|
||||
return currentLoginWizard
|
||||
?: let {
|
||||
pendingSessionData?.homeServerConnectionConfig?.let {
|
||||
DefaultLoginWizard(
|
||||
okHttpClient,
|
||||
retrofitFactory,
|
||||
coroutineDispatchers,
|
||||
sessionCreator,
|
||||
pendingSessionStore
|
||||
).also {
|
||||
currentLoginWizard = it
|
||||
}
|
||||
} ?: error("Please call getLoginFlow() with success first")
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancelPendingLoginOrRegistration() {
|
||||
currentLoginWizard = null
|
||||
currentRegistrationWizard = null
|
||||
|
||||
GlobalScope.launch(coroutineDispatchers.main) {
|
||||
// Keep only the home sever config
|
||||
pendingSessionData?.homeServerConnectionConfig
|
||||
?.let {
|
||||
pendingSessionStore.savePendingSessionData(PendingSessionData(it))
|
||||
}
|
||||
?: run {
|
||||
// Should not happen
|
||||
pendingSessionStore.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.auth
|
||||
|
||||
import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
||||
|
||||
/**
|
||||
* Store for elements when doing login or registration
|
||||
*/
|
||||
internal interface PendingSessionStore {
|
||||
|
||||
suspend fun savePendingSessionData(pendingSessionData: PendingSessionData)
|
||||
|
||||
fun getPendingSessionData(): PendingSessionData?
|
||||
|
||||
suspend fun delete()
|
||||
}
|
@ -31,7 +31,8 @@ internal interface SessionCreator {
|
||||
|
||||
internal class DefaultSessionCreator @Inject constructor(
|
||||
private val sessionParamsStore: SessionParamsStore,
|
||||
private val sessionManager: SessionManager
|
||||
private val sessionManager: SessionManager,
|
||||
private val pendingSessionStore: PendingSessionStore
|
||||
) : SessionCreator {
|
||||
|
||||
/**
|
||||
@ -39,6 +40,9 @@ internal class DefaultSessionCreator @Inject constructor(
|
||||
* identity server url if provided in the credentials
|
||||
*/
|
||||
override suspend fun createSession(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session {
|
||||
// We can cleanup the pending session params
|
||||
pendingSessionStore.delete()
|
||||
|
||||
val sessionParams = SessionParams(
|
||||
credentials = credentials,
|
||||
homeServerConnectionConfig = homeServerConnectionConfig.copy(
|
||||
|
@ -23,6 +23,7 @@ import io.realm.annotations.RealmModule
|
||||
*/
|
||||
@RealmModule(library = true,
|
||||
classes = [
|
||||
SessionParamsEntity::class
|
||||
SessionParamsEntity::class,
|
||||
PendingSessionEntity::class
|
||||
])
|
||||
internal class AuthRealmModule
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.auth.db
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.internal.auth.login.ResetPasswordData
|
||||
import im.vector.matrix.android.internal.auth.registration.ThreePidData
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* This class holds all pending data when creating a session, either by login or by register
|
||||
*/
|
||||
internal data class PendingSessionData(
|
||||
val homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
|
||||
/* ==========================================================================================
|
||||
* Common
|
||||
* ========================================================================================== */
|
||||
|
||||
val clientSecret: String = UUID.randomUUID().toString(),
|
||||
val sendAttempt: Int = 0,
|
||||
|
||||
/* ==========================================================================================
|
||||
* For login
|
||||
* ========================================================================================== */
|
||||
|
||||
val resetPasswordData: ResetPasswordData? = null,
|
||||
|
||||
/* ==========================================================================================
|
||||
* For register
|
||||
* ========================================================================================== */
|
||||
|
||||
val currentSession: String? = null,
|
||||
val currentThreePidData: ThreePidData? = null
|
||||
)
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.auth.db
|
||||
|
||||
import io.realm.RealmObject
|
||||
|
||||
internal open class PendingSessionEntity(
|
||||
var homeServerConnectionConfigJson: String = "",
|
||||
var clientSecret: String = "",
|
||||
var sendAttempt: Int = 0,
|
||||
var resetPasswordDataJson: String? = null,
|
||||
var currentSession: String? = null,
|
||||
var currentThreePidDataJson: String? = null
|
||||
) : RealmObject()
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.auth.db
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.internal.auth.login.ResetPasswordData
|
||||
import im.vector.matrix.android.internal.auth.registration.ThreePidData
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class PendingSessionMapper @Inject constructor(moshi: Moshi) {
|
||||
|
||||
private val homeServerConnectionConfigAdapter = moshi.adapter(HomeServerConnectionConfig::class.java)
|
||||
private val resetPasswordDataAdapter = moshi.adapter(ResetPasswordData::class.java)
|
||||
private val threePidDataAdapter = moshi.adapter(ThreePidData::class.java)
|
||||
|
||||
fun map(entity: PendingSessionEntity?): PendingSessionData? {
|
||||
if (entity == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val homeServerConnectionConfig = homeServerConnectionConfigAdapter.fromJson(entity.homeServerConnectionConfigJson)!!
|
||||
val resetPasswordData = entity.resetPasswordDataJson?.let { resetPasswordDataAdapter.fromJson(it) }
|
||||
val threePidData = entity.currentThreePidDataJson?.let { threePidDataAdapter.fromJson(it) }
|
||||
|
||||
return PendingSessionData(
|
||||
homeServerConnectionConfig = homeServerConnectionConfig,
|
||||
clientSecret = entity.clientSecret,
|
||||
sendAttempt = entity.sendAttempt,
|
||||
resetPasswordData = resetPasswordData,
|
||||
currentSession = entity.currentSession,
|
||||
currentThreePidData = threePidData)
|
||||
}
|
||||
|
||||
fun map(sessionData: PendingSessionData?): PendingSessionEntity? {
|
||||
if (sessionData == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val homeServerConnectionConfigJson = homeServerConnectionConfigAdapter.toJson(sessionData.homeServerConnectionConfig)
|
||||
val resetPasswordDataJson = resetPasswordDataAdapter.toJson(sessionData.resetPasswordData)
|
||||
val currentThreePidDataJson = threePidDataAdapter.toJson(sessionData.currentThreePidData)
|
||||
|
||||
return PendingSessionEntity(
|
||||
homeServerConnectionConfigJson = homeServerConnectionConfigJson,
|
||||
clientSecret = sessionData.clientSecret,
|
||||
sendAttempt = sessionData.sendAttempt,
|
||||
resetPasswordDataJson = resetPasswordDataJson,
|
||||
currentSession = sessionData.currentSession,
|
||||
currentThreePidDataJson = currentThreePidDataJson
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.matrix.android.internal.auth.db
|
||||
|
||||
import im.vector.matrix.android.internal.auth.PendingSessionStore
|
||||
import im.vector.matrix.android.internal.database.awaitTransaction
|
||||
import im.vector.matrix.android.internal.di.AuthDatabase
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RealmPendingSessionStore @Inject constructor(private val mapper: PendingSessionMapper,
|
||||
@AuthDatabase
|
||||
private val realmConfiguration: RealmConfiguration
|
||||
) : PendingSessionStore {
|
||||
|
||||
override suspend fun savePendingSessionData(pendingSessionData: PendingSessionData) {
|
||||
awaitTransaction(realmConfiguration) { realm ->
|
||||
val entity = mapper.map(pendingSessionData)
|
||||
if (entity != null) {
|
||||
realm.where(PendingSessionEntity::class.java)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
|
||||
realm.insert(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPendingSessionData(): PendingSessionData? {
|
||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
||||
realm
|
||||
.where(PendingSessionEntity::class.java)
|
||||
.findAll()
|
||||
.map { mapper.map(it) }
|
||||
.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete() {
|
||||
awaitTransaction(realmConfiguration) {
|
||||
it.where(PendingSessionEntity::class.java)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
}
|
||||
}
|
@ -17,19 +17,21 @@
|
||||
package im.vector.matrix.android.internal.auth.login
|
||||
|
||||
import android.util.Patterns
|
||||
import com.squareup.moshi.JsonClass
|
||||
import dagger.Lazy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.auth.login.LoginWizard
|
||||
import im.vector.matrix.android.api.auth.registration.RegisterThreePid
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.auth.AuthAPI
|
||||
import im.vector.matrix.android.internal.auth.PendingSessionStore
|
||||
import im.vector.matrix.android.internal.auth.SessionCreator
|
||||
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.db.PendingSessionData
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
|
||||
import im.vector.matrix.android.internal.auth.registration.RegisterAddThreePidTask
|
||||
@ -40,28 +42,25 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import java.util.*
|
||||
|
||||
// Container to store the data when a reset password is in the email validation step
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class ResetPasswordData(
|
||||
val newPassword: String,
|
||||
val addThreePidRegistrationResponse: AddThreePidRegistrationResponse
|
||||
)
|
||||
|
||||
internal class DefaultLoginWizard(
|
||||
private val homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
okHttpClient: Lazy<OkHttpClient>,
|
||||
retrofitFactory: RetrofitFactory,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionCreator: SessionCreator
|
||||
private val sessionCreator: SessionCreator,
|
||||
private val pendingSessionStore: PendingSessionStore
|
||||
) : LoginWizard {
|
||||
|
||||
private var clientSecret = UUID.randomUUID().toString()
|
||||
private var sendAttempt = 0
|
||||
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
|
||||
|
||||
private var resetPasswordData: ResetPasswordData? = null
|
||||
|
||||
private val authAPI = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
||||
private val authAPI = retrofitFactory.create(okHttpClient, pendingSessionData.homeServerConnectionConfig.homeServerUri.toString())
|
||||
.create(AuthAPI::class.java)
|
||||
|
||||
override fun login(login: String,
|
||||
@ -85,7 +84,7 @@ internal class DefaultLoginWizard(
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}
|
||||
|
||||
sessionCreator.createSession(credentials, homeServerConnectionConfig)
|
||||
sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
||||
}
|
||||
|
||||
override fun resetPassword(email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
@ -97,17 +96,25 @@ internal class DefaultLoginWizard(
|
||||
private suspend fun resetPasswordInternal(email: String, newPassword: String) {
|
||||
val param = RegisterAddThreePidTask.Params(
|
||||
RegisterThreePid.Email(email),
|
||||
clientSecret,
|
||||
sendAttempt++
|
||||
pendingSessionData.clientSecret,
|
||||
pendingSessionData.sendAttempt
|
||||
)
|
||||
|
||||
pendingSessionData = pendingSessionData.copy(
|
||||
sendAttempt = pendingSessionData.sendAttempt + 1
|
||||
).also { pendingSessionStore.savePendingSessionData(it) }
|
||||
|
||||
val result = executeRequest<AddThreePidRegistrationResponse> {
|
||||
apiCall = authAPI.resetPassword(AddThreePidRegistrationParams.from(param))
|
||||
}
|
||||
resetPasswordData = ResetPasswordData(newPassword, result)
|
||||
|
||||
pendingSessionData = pendingSessionData.copy(
|
||||
resetPasswordData = ResetPasswordData(newPassword, result)
|
||||
).also { pendingSessionStore.savePendingSessionData(it) }
|
||||
}
|
||||
|
||||
override fun resetPasswordMailConfirmed(callback: MatrixCallback<Unit>): Cancelable {
|
||||
val safeResetPasswordData = resetPasswordData ?: run {
|
||||
val safeResetPasswordData = pendingSessionData.resetPasswordData ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, no reset password in progress"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
@ -118,7 +125,7 @@ internal class DefaultLoginWizard(
|
||||
|
||||
private suspend fun resetPasswordMailConfirmedInternal(resetPasswordData: ResetPasswordData) {
|
||||
val param = ResetPasswordMailConfirmed.create(
|
||||
clientSecret,
|
||||
pendingSessionData.clientSecret,
|
||||
resetPasswordData.addThreePidRegistrationResponse.sid,
|
||||
resetPasswordData.newPassword
|
||||
)
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
package im.vector.matrix.android.internal.auth.registration
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import dagger.Lazy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.auth.registration.RegisterThreePid
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||
@ -27,50 +27,74 @@ import im.vector.matrix.android.api.failure.Failure.RegistrationFlowError
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.auth.AuthAPI
|
||||
import im.vector.matrix.android.internal.auth.PendingSessionStore
|
||||
import im.vector.matrix.android.internal.auth.SessionCreator
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.task.launchToCallback
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import okhttp3.OkHttpClient
|
||||
import java.util.*
|
||||
|
||||
// Container to store the data when a three pid is in validation step
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class ThreePidData(
|
||||
val threePid: RegisterThreePid,
|
||||
val email: String,
|
||||
val msisdn: String,
|
||||
val country: String,
|
||||
val addThreePidRegistrationResponse: AddThreePidRegistrationResponse,
|
||||
val registrationParams: RegistrationParams
|
||||
)
|
||||
) {
|
||||
val threePid: RegisterThreePid
|
||||
get() {
|
||||
return if (email.isNotBlank()) {
|
||||
RegisterThreePid.Email(email)
|
||||
} else {
|
||||
RegisterThreePid.Msisdn(msisdn, country)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(threePid: RegisterThreePid,
|
||||
addThreePidRegistrationResponse: AddThreePidRegistrationResponse,
|
||||
registrationParams: RegistrationParams): ThreePidData {
|
||||
return when (threePid) {
|
||||
is RegisterThreePid.Email ->
|
||||
ThreePidData(threePid.email, "", "", addThreePidRegistrationResponse, registrationParams)
|
||||
is RegisterThreePid.Msisdn ->
|
||||
ThreePidData("", threePid.msisdn, threePid.countryCode, addThreePidRegistrationResponse, registrationParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class execute the registration request and is responsible to keep the session of interactive authentication
|
||||
*/
|
||||
internal class DefaultRegistrationWizard(private val homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionCreator: SessionCreator) : RegistrationWizard {
|
||||
private var clientSecret = UUID.randomUUID().toString()
|
||||
private var sendAttempt = 0
|
||||
internal class DefaultRegistrationWizard(
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionCreator: SessionCreator,
|
||||
private val pendingSessionStore: PendingSessionStore
|
||||
) : RegistrationWizard {
|
||||
|
||||
private var currentSession: String? = null
|
||||
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
|
||||
|
||||
private val authAPI = buildAuthAPI()
|
||||
private val registerTask = DefaultRegisterTask(authAPI)
|
||||
private val registerAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
|
||||
private val validateCodeTask = DefaultValidateCodeTask(authAPI)
|
||||
|
||||
private var currentThreePidData: ThreePidData? = null
|
||||
|
||||
override val currentThreePid: String?
|
||||
get() {
|
||||
return when (val threePid = currentThreePidData?.threePid) {
|
||||
return when (val threePid = pendingSessionData.currentThreePidData?.threePid) {
|
||||
is RegisterThreePid.Email -> threePid.email
|
||||
is RegisterThreePid.Msisdn -> {
|
||||
// Take formatted msisdn if provided by the server
|
||||
currentThreePidData?.addThreePidRegistrationResponse?.formattedMsisdn?.takeIf { it.isNotBlank() } ?: threePid.msisdn
|
||||
pendingSessionData.currentThreePidData?.addThreePidRegistrationResponse?.formattedMsisdn?.takeIf { it.isNotBlank() } ?: threePid.msisdn
|
||||
}
|
||||
null -> null
|
||||
}
|
||||
@ -98,7 +122,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
}
|
||||
|
||||
override fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeSession = currentSession ?: run {
|
||||
val safeSession = pendingSessionData.currentSession ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
@ -109,7 +133,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
}
|
||||
|
||||
override fun acceptTerms(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeSession = currentSession ?: run {
|
||||
val safeSession = pendingSessionData.currentSession ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
@ -120,14 +144,16 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
}
|
||||
|
||||
override fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
currentThreePidData = null
|
||||
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||
pendingSessionData = pendingSessionData.copy(currentThreePidData = null)
|
||||
.also { pendingSessionStore.savePendingSessionData(it) }
|
||||
|
||||
sendThreePid(threePid)
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendAgainThreePid(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeCurrentThreePid = currentThreePidData?.threePid ?: run {
|
||||
val safeCurrentThreePid = pendingSessionData.currentThreePidData?.threePid ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
@ -137,33 +163,43 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
}
|
||||
|
||||
private suspend fun sendThreePid(threePid: RegisterThreePid): RegistrationResult {
|
||||
val safeSession = currentSession ?: throw IllegalStateException("developer error, call createAccount() method first")
|
||||
val response = registerAddThreePidTask.execute(RegisterAddThreePidTask.Params(threePid, clientSecret, sendAttempt++))
|
||||
val safeSession = pendingSessionData.currentSession ?: throw IllegalStateException("developer error, call createAccount() method first")
|
||||
val response = registerAddThreePidTask.execute(
|
||||
RegisterAddThreePidTask.Params(
|
||||
threePid,
|
||||
pendingSessionData.clientSecret,
|
||||
pendingSessionData.sendAttempt))
|
||||
|
||||
pendingSessionData = pendingSessionData.copy(sendAttempt = pendingSessionData.sendAttempt + 1)
|
||||
.also { pendingSessionStore.savePendingSessionData(it) }
|
||||
|
||||
val params = RegistrationParams(
|
||||
auth = if (threePid is RegisterThreePid.Email) {
|
||||
AuthParams.createForEmailIdentity(safeSession,
|
||||
ThreePidCredentials(
|
||||
clientSecret = clientSecret,
|
||||
clientSecret = pendingSessionData.clientSecret,
|
||||
sid = response.sid
|
||||
)
|
||||
)
|
||||
} else {
|
||||
AuthParams.createForMsisdnIdentity(safeSession,
|
||||
ThreePidCredentials(
|
||||
clientSecret = clientSecret,
|
||||
clientSecret = pendingSessionData.clientSecret,
|
||||
sid = response.sid
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
// Store data
|
||||
currentThreePidData = ThreePidData(threePid, response, params)
|
||||
pendingSessionData = pendingSessionData.copy(currentThreePidData = ThreePidData.from(threePid, response, params))
|
||||
.also { pendingSessionStore.savePendingSessionData(it) }
|
||||
|
||||
// and send the sid a first time
|
||||
return performRegistrationRequest(params)
|
||||
}
|
||||
|
||||
override fun checkIfEmailHasBeenValidated(delayMillis: Long, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeParam = currentThreePidData?.registrationParams ?: run {
|
||||
val safeParam = pendingSessionData.currentThreePidData?.registrationParams ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
@ -179,11 +215,12 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
}
|
||||
|
||||
private suspend fun validateThreePid(code: String): RegistrationResult {
|
||||
val registrationParams = currentThreePidData?.registrationParams ?: throw IllegalStateException("developer error, no pending three pid")
|
||||
val safeCurrentData = currentThreePidData ?: throw IllegalStateException("developer error, call createAccount() method first")
|
||||
val registrationParams = pendingSessionData.currentThreePidData?.registrationParams
|
||||
?: throw IllegalStateException("developer error, no pending three pid")
|
||||
val safeCurrentData = pendingSessionData.currentThreePidData ?: throw IllegalStateException("developer error, call createAccount() method first")
|
||||
val url = safeCurrentData.addThreePidRegistrationResponse.submitUrl ?: throw IllegalStateException("Missing url the send the code")
|
||||
val validationBody = ValidationCodeBody(
|
||||
clientSecret = clientSecret,
|
||||
clientSecret = pendingSessionData.clientSecret,
|
||||
sid = safeCurrentData.addThreePidRegistrationResponse.sid,
|
||||
code = code
|
||||
)
|
||||
@ -199,7 +236,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
}
|
||||
|
||||
override fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeSession = currentSession ?: run {
|
||||
val safeSession = pendingSessionData.currentSession ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
@ -216,19 +253,20 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
registerTask.execute(RegisterTask.Params(registrationParams))
|
||||
} catch (exception: Throwable) {
|
||||
if (exception is RegistrationFlowError) {
|
||||
currentSession = exception.registrationFlowResponse.session
|
||||
pendingSessionData = pendingSessionData.copy(currentSession = exception.registrationFlowResponse.session)
|
||||
.also { pendingSessionStore.savePendingSessionData(it) }
|
||||
return RegistrationResult.FlowResponse(exception.registrationFlowResponse.toFlowResult())
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
|
||||
val session = sessionCreator.createSession(credentials, homeServerConnectionConfig)
|
||||
val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
||||
return RegistrationResult.Success(session)
|
||||
}
|
||||
|
||||
private fun buildAuthAPI(): AuthAPI {
|
||||
val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
||||
val retrofit = retrofitFactory.create(okHttpClient, pendingSessionData.homeServerConnectionConfig.homeServerUri.toString())
|
||||
return retrofit.create(AuthAPI::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -73,8 +73,11 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
var isRegistrationStarted: Boolean = false
|
||||
private set
|
||||
|
||||
private var registrationWizard: RegistrationWizard? = null
|
||||
private var loginWizard: LoginWizard? = null
|
||||
private val registrationWizard: RegistrationWizard?
|
||||
get() = authenticationService.getRegistrationWizard()
|
||||
|
||||
private val loginWizard: LoginWizard?
|
||||
get() = authenticationService.getLoginWizard()
|
||||
|
||||
private var loginConfig: LoginConfig? = null
|
||||
|
||||
@ -254,8 +257,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
}
|
||||
}
|
||||
LoginAction.ResetHomeServerUrl -> {
|
||||
registrationWizard = null
|
||||
loginWizard = null
|
||||
authenticationService.cancelPendingLoginOrRegistration()
|
||||
|
||||
setState {
|
||||
copy(
|
||||
@ -449,13 +451,12 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
)
|
||||
}
|
||||
|
||||
registrationWizard = authenticationService.getOrCreateRegistrationWizard()
|
||||
|
||||
currentTask = registrationWizard?.getRegistrationFlow(registrationCallback)
|
||||
}
|
||||
|
||||
private fun startAuthenticationFlow() {
|
||||
loginWizard = authenticationService.createLoginWizard()
|
||||
// No op
|
||||
loginWizard
|
||||
}
|
||||
|
||||
private fun onFlowResponse(flowResult: FlowResult) {
|
||||
@ -507,8 +508,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
} else {
|
||||
currentTask?.cancel()
|
||||
currentTask = null
|
||||
loginWizard = null
|
||||
registrationWizard = null
|
||||
authenticationService.cancelPendingLoginOrRegistration()
|
||||
|
||||
setState {
|
||||
copy(
|
||||
|
Loading…
x
Reference in New Issue
Block a user