Feature/5575 custom auth params for sign up (#5577)

Add a fun `RegistrationWizard.registrationCustom()` to be able to use any parameters during the registration.
Move terms converter into `api` package.
This commit is contained in:
TarasSmakula 2022-04-21 23:02:27 +03:00 committed by GitHub
parent 96350b0ed0
commit 2839d1467f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 268 additions and 138 deletions

2
changelog.d/5575.sdk Normal file
View file

@ -0,0 +1,2 @@
- Added registrationCustom into RegistrationWizard to send custom auth params for sign up
- Moved terms converter into api package to make it accessible in sdk

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2019 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.login.terms package org.matrix.android.sdk.api.auth
data class UrlAndName( data class UrlAndName(
val url: String, val url: String,

View file

@ -1,127 +1,127 @@
/* /*
* Copyright 2019 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.login.terms package org.matrix.android.sdk.api.auth
import org.matrix.android.sdk.api.auth.data.LocalizedFlowDataLoginTerms import org.matrix.android.sdk.api.auth.data.LocalizedFlowDataLoginTerms
import org.matrix.android.sdk.api.auth.registration.TermPolicies import org.matrix.android.sdk.api.auth.registration.TermPolicies
/** /**
* This method extract the policies from the login terms parameter, regarding the user language. * This method extract the policies from the login terms parameter, regarding the user language.
* For each policy, if user language is not found, the default language is used and if not found, the first url and name are used (not predictable) * For each policy, if user language is not found, the default language is used and if not found, the first url and name are used (not predictable)
* *
* Example of Data: * Example of Data:
* <pre> * <pre>
* "m.login.terms": { * "m.login.terms": {
* "policies": { * "policies": {
* "privacy_policy": { * "privacy_policy": {
* "version": "1.0", * "version": "1.0",
* "en": { * "en": {
* "url": "http:\/\/matrix.org\/_matrix\/consent?v=1.0", * "url": "http:\/\/matrix.org\/_matrix\/consent?v=1.0",
* "name": "Terms and Conditions" * "name": "Terms and Conditions"
* } * }
* } * }
* } * }
* } * }
*</pre> *</pre>
* *
* @param userLanguage the user language * @param userLanguage the user language
* @param defaultLanguage the default language to use if the user language is not found for a policy in registrationFlowResponse * @param defaultLanguage the default language to use if the user language is not found for a policy in registrationFlowResponse
*/ */
fun TermPolicies.toLocalizedLoginTerms(userLanguage: String, fun TermPolicies.toLocalizedLoginTerms(userLanguage: String,
defaultLanguage: String = "en"): List<LocalizedFlowDataLoginTerms> { defaultLanguage: String = "en"): List<LocalizedFlowDataLoginTerms> {
val result = ArrayList<LocalizedFlowDataLoginTerms>() val result = ArrayList<LocalizedFlowDataLoginTerms>()
val policies = get("policies") val policies = get("policies")
if (policies is Map<*, *>) { if (policies is Map<*, *>) {
policies.keys.forEach { policyName -> policies.keys.forEach { policyName ->
val localizedFlowDataLoginTermsPolicyName = policyName as String val localizedFlowDataLoginTermsPolicyName = policyName as String
var localizedFlowDataLoginTermsVersion: String? = null var localizedFlowDataLoginTermsVersion: String? = null
var localizedFlowDataLoginTermsLocalizedUrl: String? = null var localizedFlowDataLoginTermsLocalizedUrl: String? = null
var localizedFlowDataLoginTermsLocalizedName: String? = null var localizedFlowDataLoginTermsLocalizedName: String? = null
val policy = policies[policyName] val policy = policies[policyName]
// Enter this policy // Enter this policy
if (policy is Map<*, *>) { if (policy is Map<*, *>) {
// Version // Version
localizedFlowDataLoginTermsVersion = policy["version"] as String? localizedFlowDataLoginTermsVersion = policy["version"] as String?
var userLanguageUrlAndName: UrlAndName? = null var userLanguageUrlAndName: UrlAndName? = null
var defaultLanguageUrlAndName: UrlAndName? = null var defaultLanguageUrlAndName: UrlAndName? = null
var firstUrlAndName: UrlAndName? = null var firstUrlAndName: UrlAndName? = null
// Search for language // Search for language
policy.keys.forEach { policyKey -> policy.keys.forEach { policyKey ->
when (policyKey) { when (policyKey) {
"version" -> Unit // Ignore "version" -> Unit // Ignore
userLanguage -> { userLanguage -> {
// We found the data for the user language // We found the data for the user language
userLanguageUrlAndName = extractUrlAndName(policy[policyKey]) userLanguageUrlAndName = extractUrlAndName(policy[policyKey])
} }
defaultLanguage -> { defaultLanguage -> {
// We found default language // We found default language
defaultLanguageUrlAndName = extractUrlAndName(policy[policyKey]) defaultLanguageUrlAndName = extractUrlAndName(policy[policyKey])
} }
else -> { else -> {
if (firstUrlAndName == null) { if (firstUrlAndName == null) {
// Get at least some data // Get at least some data
firstUrlAndName = extractUrlAndName(policy[policyKey]) firstUrlAndName = extractUrlAndName(policy[policyKey])
} }
} }
} }
} }
// Copy found language data by priority // Copy found language data by priority
when { when {
userLanguageUrlAndName != null -> { userLanguageUrlAndName != null -> {
localizedFlowDataLoginTermsLocalizedUrl = userLanguageUrlAndName!!.url localizedFlowDataLoginTermsLocalizedUrl = userLanguageUrlAndName!!.url
localizedFlowDataLoginTermsLocalizedName = userLanguageUrlAndName!!.name localizedFlowDataLoginTermsLocalizedName = userLanguageUrlAndName!!.name
} }
defaultLanguageUrlAndName != null -> { defaultLanguageUrlAndName != null -> {
localizedFlowDataLoginTermsLocalizedUrl = defaultLanguageUrlAndName!!.url localizedFlowDataLoginTermsLocalizedUrl = defaultLanguageUrlAndName!!.url
localizedFlowDataLoginTermsLocalizedName = defaultLanguageUrlAndName!!.name localizedFlowDataLoginTermsLocalizedName = defaultLanguageUrlAndName!!.name
} }
firstUrlAndName != null -> { firstUrlAndName != null -> {
localizedFlowDataLoginTermsLocalizedUrl = firstUrlAndName!!.url localizedFlowDataLoginTermsLocalizedUrl = firstUrlAndName!!.url
localizedFlowDataLoginTermsLocalizedName = firstUrlAndName!!.name localizedFlowDataLoginTermsLocalizedName = firstUrlAndName!!.name
} }
} }
} }
result.add(LocalizedFlowDataLoginTerms( result.add(LocalizedFlowDataLoginTerms(
policyName = localizedFlowDataLoginTermsPolicyName, policyName = localizedFlowDataLoginTermsPolicyName,
version = localizedFlowDataLoginTermsVersion, version = localizedFlowDataLoginTermsVersion,
localizedUrl = localizedFlowDataLoginTermsLocalizedUrl, localizedUrl = localizedFlowDataLoginTermsLocalizedUrl,
localizedName = localizedFlowDataLoginTermsLocalizedName localizedName = localizedFlowDataLoginTermsLocalizedName
)) ))
} }
} }
return result return result
} }
private fun extractUrlAndName(policyData: Any?): UrlAndName? { private fun extractUrlAndName(policyData: Any?): UrlAndName? {
if (policyData is Map<*, *>) { if (policyData is Map<*, *>) {
val url = policyData["url"] as String? val url = policyData["url"] as String?
val name = policyData["name"] as String? val name = policyData["name"] as String?
if (url != null && name != null) { if (url != null && name != null) {
return UrlAndName(url, name) return UrlAndName(url, name)
} }
} }
return null return null
} }

View file

@ -16,6 +16,8 @@
package org.matrix.android.sdk.api.auth.registration package org.matrix.android.sdk.api.auth.registration
import org.matrix.android.sdk.api.util.JsonDict
/** /**
* Set of methods to be able to create an account on a homeserver. * Set of methods to be able to create an account on a homeserver.
* *
@ -73,6 +75,13 @@ interface RegistrationWizard {
*/ */
suspend fun dummy(): RegistrationResult suspend fun dummy(): RegistrationResult
/**
* Perform custom registration stage by sending a custom JsonDict.
* Current registration "session" param will be included into authParams by default.
* The authParams should contain at least one entry "type" with a String value.
*/
suspend fun registrationCustom(authParams: JsonDict): RegistrationResult
/** /**
* Perform the "m.login.email.identity" or "m.login.msisdn" stage. * Perform the "m.login.email.identity" or "m.login.msisdn" stage.
* *

View file

@ -26,6 +26,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
import org.matrix.android.sdk.internal.auth.login.ResetPasswordMailConfirmed import org.matrix.android.sdk.internal.auth.login.ResetPasswordMailConfirmed
import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationParams import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationParams
import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationResponse import org.matrix.android.sdk.internal.auth.registration.AddThreePidRegistrationResponse
import org.matrix.android.sdk.internal.auth.registration.RegistrationCustomParams
import org.matrix.android.sdk.internal.auth.registration.RegistrationParams import org.matrix.android.sdk.internal.auth.registration.RegistrationParams
import org.matrix.android.sdk.internal.auth.registration.SuccessResult import org.matrix.android.sdk.internal.auth.registration.SuccessResult
import org.matrix.android.sdk.internal.auth.registration.ValidationCodeBody import org.matrix.android.sdk.internal.auth.registration.ValidationCodeBody
@ -68,6 +69,14 @@ internal interface AuthAPI {
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register") @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
suspend fun register(@Body registrationParams: RegistrationParams): Credentials suspend fun register(@Body registrationParams: RegistrationParams): Credentials
/**
* Register to the homeserver, or get error 401 with a RegistrationFlowResponse object if registration is incomplete
* method to perform other custom stages
* Ref: https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
*/
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
suspend fun registerCustom(@Body registrationCustomParams: RegistrationCustomParams): Credentials
/** /**
* Checks to see if a username is available, and valid, for the server. * Checks to see if a username is available, and valid, for the server.
*/ */

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.auth.registration package org.matrix.android.sdk.internal.auth.registration
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability import org.matrix.android.sdk.api.auth.registration.RegistrationAvailability
@ -25,6 +26,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
import org.matrix.android.sdk.api.auth.registration.toFlowResult import org.matrix.android.sdk.api.auth.registration.toFlowResult
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError import org.matrix.android.sdk.api.failure.Failure.RegistrationFlowError
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.auth.AuthAPI import org.matrix.android.sdk.internal.auth.AuthAPI
import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.auth.PendingSessionStore
import org.matrix.android.sdk.internal.auth.SessionCreator import org.matrix.android.sdk.internal.auth.SessionCreator
@ -45,6 +47,7 @@ internal class DefaultRegistrationWizard(
private val registerAvailableTask: RegisterAvailableTask = DefaultRegisterAvailableTask(authAPI) private val registerAvailableTask: RegisterAvailableTask = DefaultRegisterAvailableTask(authAPI)
private val registerAddThreePidTask: RegisterAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI) private val registerAddThreePidTask: RegisterAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
private val validateCodeTask: ValidateCodeTask = DefaultValidateCodeTask(authAPI) private val validateCodeTask: ValidateCodeTask = DefaultValidateCodeTask(authAPI)
private val registerCustomTask: RegisterCustomTask = DefaultRegisterCustomTask(authAPI)
override val currentThreePid: String? override val currentThreePid: String?
get() { get() {
@ -187,22 +190,51 @@ internal class DefaultRegistrationWizard(
return performRegistrationRequest(params) return performRegistrationRequest(params)
} }
private suspend fun performRegistrationRequest(registrationParams: RegistrationParams, override suspend fun registrationCustom(
delayMillis: Long = 0): RegistrationResult { authParams: JsonDict
): RegistrationResult {
val safeSession = pendingSessionData.currentSession
?: throw IllegalStateException("developer error, call createAccount() method first")
val mutableParams = authParams.toMutableMap()
mutableParams["session"] = safeSession
val params = RegistrationCustomParams(auth = mutableParams)
return performRegistrationOtherRequest(params)
}
private suspend fun performRegistrationRequest(
registrationParams: RegistrationParams,
delayMillis: Long = 0
): RegistrationResult {
delay(delayMillis) delay(delayMillis)
return register { registerTask.execute(RegisterTask.Params(registrationParams)) }
}
private suspend fun performRegistrationOtherRequest(
registrationCustomParams: RegistrationCustomParams
): RegistrationResult {
return register { registerCustomTask.execute(RegisterCustomTask.Params(registrationCustomParams)) }
}
private suspend fun register(
execute: suspend () -> Credentials
): RegistrationResult {
val credentials = try { val credentials = try {
registerTask.execute(RegisterTask.Params(registrationParams)) execute.invoke()
} catch (exception: Throwable) { } catch (exception: Throwable) {
if (exception is RegistrationFlowError) { if (exception is RegistrationFlowError) {
pendingSessionData = pendingSessionData.copy(currentSession = exception.registrationFlowResponse.session) pendingSessionData =
.also { pendingSessionStore.savePendingSessionData(it) } pendingSessionData.copy(currentSession = exception.registrationFlowResponse.session)
.also { pendingSessionStore.savePendingSessionData(it) }
return RegistrationResult.FlowResponse(exception.registrationFlowResponse.toFlowResult()) return RegistrationResult.FlowResponse(exception.registrationFlowResponse.toFlowResult())
} else { } else {
throw exception throw exception
} }
} }
val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) val session =
sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
return RegistrationResult.Success(session) return RegistrationResult.Success(session)
} }

View file

@ -0,0 +1,47 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* 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.android.sdk.internal.auth.registration
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse
import org.matrix.android.sdk.internal.auth.AuthAPI
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.task.Task
internal interface RegisterCustomTask : Task<RegisterCustomTask.Params, Credentials> {
data class Params(
val registrationCustomParams: RegistrationCustomParams
)
}
internal class DefaultRegisterCustomTask(
private val authAPI: AuthAPI
) : RegisterCustomTask {
override suspend fun execute(params: RegisterCustomTask.Params): Credentials {
try {
return executeRequest(null) {
authAPI.registerCustom(params.registrationCustomParams)
}
} catch (throwable: Throwable) {
throw throwable.toRegistrationFlowResponse()
?.let { Failure.RegistrationFlowError(it) }
?: throwable
}
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2021 The Matrix.org Foundation C.I.C.
*
* 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.android.sdk.internal.auth.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.util.JsonDict
/**
* Class to pass parameters to the custom registration types for /register.
*/
@JsonClass(generateAdapter = true)
internal data class RegistrationCustomParams(
// authentication parameters
@Json(name = "auth")
val auth: JsonDict? = null,
)

View file

@ -42,10 +42,10 @@ import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeActivity
import im.vector.app.features.login.terms.LoginTermsFragment import im.vector.app.features.login.terms.LoginTermsFragment
import im.vector.app.features.login.terms.LoginTermsFragmentArgument import im.vector.app.features.login.terms.LoginTermsFragmentArgument
import im.vector.app.features.login.terms.toLocalizedLoginTerms
import im.vector.app.features.pin.UnlockedActivity import im.vector.app.features.pin.UnlockedActivity
import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.FlowResult
import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.auth.registration.Stage
import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
/** /**

View file

@ -41,7 +41,6 @@ import im.vector.app.features.login.LoginWaitForEmailFragmentArgument
import im.vector.app.features.login.TextInputFormFragmentMode import im.vector.app.features.login.TextInputFormFragmentMode
import im.vector.app.features.login.isSupported import im.vector.app.features.login.isSupported
import im.vector.app.features.login.terms.LoginTermsFragmentArgument import im.vector.app.features.login.terms.LoginTermsFragmentArgument
import im.vector.app.features.login.terms.toLocalizedLoginTerms
import im.vector.app.features.login2.LoginAction2 import im.vector.app.features.login2.LoginAction2
import im.vector.app.features.login2.LoginCaptchaFragment2 import im.vector.app.features.login2.LoginCaptchaFragment2
import im.vector.app.features.login2.LoginFragmentSigninPassword2 import im.vector.app.features.login2.LoginFragmentSigninPassword2
@ -66,6 +65,7 @@ import im.vector.app.features.login2.created.AccountCreatedFragment
import im.vector.app.features.login2.terms.LoginTermsFragment2 import im.vector.app.features.login2.terms.LoginTermsFragment2
import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.FlowResult
import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.auth.registration.Stage
import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG" private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG"

View file

@ -45,7 +45,6 @@ import im.vector.app.features.login.ServerType
import im.vector.app.features.login.SignMode import im.vector.app.features.login.SignMode
import im.vector.app.features.login.TextInputFormFragmentMode import im.vector.app.features.login.TextInputFormFragmentMode
import im.vector.app.features.login.isSupported import im.vector.app.features.login.isSupported
import im.vector.app.features.login.terms.toLocalizedLoginTerms
import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingAction
import im.vector.app.features.onboarding.OnboardingActivity import im.vector.app.features.onboarding.OnboardingActivity
import im.vector.app.features.onboarding.OnboardingVariant import im.vector.app.features.onboarding.OnboardingVariant
@ -57,6 +56,7 @@ import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsFragment
import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsLegacyStyleFragmentArgument import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsLegacyStyleFragmentArgument
import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.FlowResult
import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.auth.registration.Stage
import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG" private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG"