Registration flow: SDK side

This commit is contained in:
Benoit Marty 2019-11-13 18:40:17 +01:00
parent 8b63f78d76
commit 4485d1c685
15 changed files with 517 additions and 1 deletions

View file

@ -4,6 +4,7 @@
<w>backstack</w>
<w>bytearray</w>
<w>ciphertext</w>
<w>coroutine</w>
<w>decryptor</w>
<w>emoji</w>
<w>emojis</w>
@ -12,6 +13,7 @@
<w>linkified</w>
<w>linkify</w>
<w>megolm</w>
<w>msisdn</w>
<w>pbkdf</w>
<w>pkcs</w>
</words>

View file

@ -30,4 +30,5 @@ data class Credentials(
@Json(name = "home_server") val homeServer: String,
@Json(name = "access_token") val accessToken: String,
@Json(name = "refresh_token") val refreshToken: String?,
@Json(name = "device_id") val deviceId: String?)
@Json(name = "device_id") val deviceId: String?
)

View file

@ -0,0 +1,25 @@
/*
* 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.api.auth.registration
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
interface RegistrationService {
fun getOrCreateRegistrationWizard(homeServerConnectionConfig: HomeServerConnectionConfig): RegistrationWizard
}

View file

@ -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.api.auth.registration
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable
interface RegistrationWizard {
fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<Session>): Cancelable
fun performReCaptcha(response: String, callback: MatrixCallback<Session>): Cancelable
// TODO Add other method here
}

View file

@ -0,0 +1,51 @@
/*
* 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.api.auth.registration
import im.vector.matrix.android.api.util.JsonDict
sealed class Stage(open val mandatory: Boolean) {
// m.login.password
data class Password(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
// m.login.recaptcha
data class ReCaptcha(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
// m.login.oauth2
// m.login.email.identity
data class Email(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
// m.login.msisdn
data class Msisdn(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
// m.login.token
// m.login.dummy
// Undocumented yet: m.login.terms
data class Terms(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
// TODO SSO
// For unknown stages
data class Other(override val mandatory: Boolean, val type: String, val params: JsonDict?) : Stage(mandatory)
}
class TermPolicies {
}

View file

@ -29,3 +29,6 @@ interface Cancelable {
// no-op
}
}
object NoOpCancellable : Cancelable

View file

@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.auth
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.registration.RegistrationParams
import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.Body
@ -31,6 +32,13 @@ import retrofit2.http.POST
*/
internal interface AuthAPI {
/**
* Register to the homeserver
* Ref: https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
fun register(registrationParams: RegistrationParams): Call<Credentials>
/**
* Get the supported login flow
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-login

View file

@ -0,0 +1,32 @@
/*
* Copyright 2018 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.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Open class, parent to all possible authentication parameters
*/
@JsonClass(generateAdapter = true)
open class AuthParams(
@Json(name = "type")
val type: String,
@Json(name = "session")
val session: String
)

View file

@ -0,0 +1,30 @@
/*
* Copyright 2018 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.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
/**
* Class to define the authentication parameters for "m.login.recaptcha" type
*/
@JsonClass(generateAdapter = true)
class AuthParamsCaptcha(session: String,
@Json(name = "response")
val response: String)
: AuthParams(LoginFlowTypes.RECAPTCHA, session)

View file

@ -0,0 +1,40 @@
/*
* Copyright 2018 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.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
/**
* Class to define the authentication parameters for "m.login.email.identity" type
*/
@JsonClass(generateAdapter = true)
class AuthParamsEmailIdentity(session: String,
@Json(name = "threepid_creds")
val threePidCredentials: ThreePidCredentials)
: AuthParams(LoginFlowTypes.EMAIL_IDENTITY, session)
data class ThreePidCredentials(
@Json(name = "client_secret")
val clientSecret: String? = null,
@Json(name = "id_server")
val idServer: String? = null,
val sid: String? = null
)

View file

@ -0,0 +1,46 @@
/*
* 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.registration
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.auth.SessionParamsStore
import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import okhttp3.OkHttpClient
import javax.inject.Provider
internal class DefaultRegistrationService(@Unauthenticated
private val okHttpClient: Provider<OkHttpClient>,
private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionParamsStore: SessionParamsStore,
private val sessionManager: SessionManager) : RegistrationService {
override fun getOrCreateRegistrationWizard(homeServerConnectionConfig: HomeServerConnectionConfig): RegistrationWizard {
// TODO Persist the wizard?
return DefaultRegistrationWizard(homeServerConnectionConfig,
okHttpClient,
retrofitFactory,
coroutineDispatchers,
sessionParamsStore,
sessionManager)
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright 2018 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.registration
import im.vector.matrix.android.api.MatrixCallback
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.registration.RegistrationWizard
import im.vector.matrix.android.api.failure.Failure
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.SessionManager
import im.vector.matrix.android.internal.auth.AuthAPI
import im.vector.matrix.android.internal.auth.SessionParamsStore
import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.util.CancelableCoroutine
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import javax.inject.Provider
internal class DefaultRegistrationWizard(private val homeServerConnectionConfig: HomeServerConnectionConfig,
@Unauthenticated
private val okHttpClient: Provider<OkHttpClient>,
private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionParamsStore: SessionParamsStore,
private val sessionManager: SessionManager) : RegistrationWizard {
private var currentSession: String? = null
private val authAPI = buildAuthAPI()
private val registerTask = DefaultRegisterTask(authAPI)
override fun createAccount(userName: String,
password: String,
initialDeviceDisplayName: String?,
callback: MatrixCallback<Session>): Cancelable {
return performRegistrationRequest(RegistrationParams(
username = userName,
password = password,
initialDeviceDisplayName = initialDeviceDisplayName
), callback)
}
override fun performReCaptcha(response: String, callback: MatrixCallback<Session>): Cancelable {
val safeSession = currentSession ?: run {
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
return NoOpCancellable
}
return performRegistrationRequest(
RegistrationParams(
auth = AuthParamsCaptcha(
session = safeSession,
response = response)
), callback)
}
private fun performRegistrationRequest(registrationParams: RegistrationParams, callback: MatrixCallback<Session>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) {
val result = runCatching {
registerTask.execute(RegisterTask.Params(registrationParams))
}
result.fold(
{
val sessionParams = SessionParams(it, homeServerConnectionConfig)
sessionParamsStore.save(sessionParams)
val session = sessionManager.getOrCreateSession(sessionParams)
callback.onSuccess(session)
},
{
if (it is Failure.RegistrationFlowError) {
currentSession = it.registrationFlowResponse.session
}
callback.onFailure(it)
}
)
}
return CancelableCoroutine(job)
}
private fun buildAuthAPI(): AuthAPI {
val retrofit = retrofitFactory.create(okHttpClient.get(), homeServerConnectionConfig.homeServerUri.toString())
return retrofit.create(AuthAPI::class.java)
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2018 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 org.matrix.androidsdk.rest.model.login
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
/**
* This class represent a localized privacy policy for registration Flow.
*/
@Parcelize
data class LocalizedFlowDataLoginTerms(
var policyName: String? = null,
var version: String? = null,
var localizedUrl: String? = null,
var localizedName: String? = null
) : Parcelable

View file

@ -0,0 +1,63 @@
/*
* 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.registration
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.internal.auth.AuthAPI
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject
internal interface RegisterTask : Task<RegisterTask.Params, Credentials> {
data class Params(
val registrationParams: RegistrationParams
)
}
internal class DefaultRegisterTask @Inject constructor(private val authAPI: AuthAPI)
: RegisterTask {
override suspend fun execute(params: RegisterTask.Params): Credentials {
try {
return executeRequest {
apiCall = authAPI.register(params.registrationParams)
}
} catch (throwable: Throwable) {
if (throwable is Failure.OtherServerError && throwable.httpCode == 401) {
// Parse to get a RegistrationFlowResponse
val registrationFlowResponse = try {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
.fromJson(throwable.errorBody)
} catch (e: Exception) {
null
}
// check if the server response can be cast
if (registrationFlowResponse != null) {
throw Failure.RegistrationFlowError(registrationFlowResponse)
} else {
throw throwable
}
} else {
// Other error
throw throwable
}
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2014 OpenMarket Ltd
* Copyright 2017 Vector Creations Ltd
* Copyright 2018 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.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Class to pass parameters to the different registration types for /register.
*/
@JsonClass(generateAdapter = true)
data class RegistrationParams(
// authentication parameters
@Json(name = "auth")
val auth: AuthParams? = null,
// the account username
@Json(name = "username")
val username: String? = null,
// the account password
@Json(name = "password")
val password: String? = null,
// device name
@Json(name = "initial_device_display_name")
val initialDeviceDisplayName: String? = null,
// Temporary flag to notify the server that we support msisdn flow. Used to prevent old app
// versions to end up in fallback because the HS returns the msisdn flow which they don't support
val x_show_msisdn: Boolean? = null
)