mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 12:00:03 +03:00
Updated implementation including outbound link for account management
This commit is contained in:
parent
bfed447b21
commit
26d71e214a
30 changed files with 179 additions and 52 deletions
|
@ -1062,6 +1062,9 @@
|
|||
<string name="settings_discovery_category">Discovery</string>
|
||||
<string name="settings_discovery_manage">Manage your discovery settings.</string>
|
||||
|
||||
<string name="settings_external_account_management_title">Account</string>
|
||||
<string name="settings_external_account_management">Manage your account at %1$s.</string>
|
||||
|
||||
<!-- analytics -->
|
||||
<string name="settings_analytics">Analytics</string>
|
||||
<string name="settings_opt_in_of_analytics">Send analytics data</string>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2023 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.api.auth.data
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* https://github.com/matrix-org/matrix-spec-proposals/pull/2965
|
||||
* <pre>
|
||||
* {
|
||||
* "issuer": "https://id.server.org",
|
||||
* "account": "https://id.server.org/my-account",
|
||||
* }
|
||||
* </pre>
|
||||
* .
|
||||
*/
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class DelegatedAuthConfig(
|
||||
@Json(name = "issuer")
|
||||
val issuer: String,
|
||||
|
||||
@Json(name = "account")
|
||||
val accountManagementUrl: String,
|
||||
)
|
|
@ -54,5 +54,11 @@ data class WellKnown(
|
|||
val identityServer: WellKnownBaseConfig? = null,
|
||||
|
||||
@Json(name = "m.integrations")
|
||||
val integrations: JsonDict? = null
|
||||
val integrations: JsonDict? = null,
|
||||
|
||||
/**
|
||||
* For delegation of auth via OIDC as per [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965)
|
||||
*/
|
||||
@Json(name = "org.matrix.msc2965.authentication")
|
||||
val unstableDelegatedAuthConfig: DelegatedAuthConfig? = null,
|
||||
)
|
||||
|
|
|
@ -75,6 +75,10 @@ data class HomeServerCapabilities(
|
|||
* True if the home server supports remote toggle of Pusher for a given device.
|
||||
*/
|
||||
val canRemotelyTogglePushNotificationsOfDevices: Boolean = false,
|
||||
/**
|
||||
* External account management url for use with MSC3824 delegated OIDC, provided in Wellknown.
|
||||
*/
|
||||
val externalAccountManagementUrl: String? = null,
|
||||
) {
|
||||
|
||||
enum class RoomCapabilitySupport {
|
||||
|
|
|
@ -105,7 +105,7 @@ internal class DefaultAuthenticationService @Inject constructor(
|
|||
appendParamToUrl("device_id", it)
|
||||
}
|
||||
|
||||
// MSC3824 action param
|
||||
// unstable MSC3824 action param
|
||||
appendParamToUrl("org.matrix.msc3824.action", action.toString())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo044
|
|||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo045
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo046
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo047
|
||||
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo048
|
||||
import org.matrix.android.sdk.internal.util.Normalizer
|
||||
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
||||
import javax.inject.Inject
|
||||
|
@ -72,7 +73,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
private val normalizer: Normalizer
|
||||
) : MatrixRealmMigration(
|
||||
dbName = "Session",
|
||||
schemaVersion = 47L,
|
||||
schemaVersion = 48L,
|
||||
) {
|
||||
/**
|
||||
* Forces all RealmSessionStoreMigration instances to be equal.
|
||||
|
@ -129,5 +130,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
|
|||
if (oldVersion < 45) MigrateSessionTo045(realm).perform()
|
||||
if (oldVersion < 46) MigrateSessionTo046(realm).perform()
|
||||
if (oldVersion < 47) MigrateSessionTo047(realm).perform()
|
||||
if (oldVersion < 48) MigrateSessionTo048(realm).perform()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ internal object HomeServerCapabilitiesMapper {
|
|||
canLoginWithQrCode = entity.canLoginWithQrCode,
|
||||
canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications,
|
||||
canRemotelyTogglePushNotificationsOfDevices = entity.canRemotelyTogglePushNotificationsOfDevices,
|
||||
externalAccountManagementUrl = entity.externalAccountManagementUrl,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.database.migration
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
||||
import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
|
||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
||||
|
||||
internal class MigrateSessionTo048(realm: DynamicRealm) : RealmMigrator(realm, 40) {
|
||||
|
||||
override fun doMigrate(realm: DynamicRealm) {
|
||||
realm.schema.get("HomeServerCapabilitiesEntity")
|
||||
?.addField(HomeServerCapabilitiesEntityFields.EXTERNAL_ACCOUNT_MANAGEMENT_URL, String::class.java)
|
||||
?.forceRefreshOfHomeServerCapabilities()
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ internal open class HomeServerCapabilitiesEntity(
|
|||
var canLoginWithQrCode: Boolean = false,
|
||||
var canUseThreadReadReceiptsAndNotifications: Boolean = false,
|
||||
var canRemotelyTogglePushNotificationsOfDevices: Boolean = false,
|
||||
var externalAccountManagementUrl: String? = null,
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
|
|
|
@ -164,6 +164,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
|
|||
Timber.v("Extracted integration config : $config")
|
||||
realm.insertOrUpdate(config)
|
||||
}
|
||||
homeServerCapabilitiesEntity.externalAccountManagementUrl = getWellknownResult.wellKnown.unstableDelegatedAuthConfig?.accountManagementUrl
|
||||
}
|
||||
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
|
||||
}
|
||||
|
|
|
@ -69,7 +69,8 @@ sealed class LoginAction : VectorViewModelAction {
|
|||
data class SetupSsoForSessionRecovery(
|
||||
val homeServerUrl: String,
|
||||
val deviceId: String,
|
||||
val ssoIdentityProviders: List<SsoIdentityProvider>?
|
||||
val ssoIdentityProviders: List<SsoIdentityProvider>?,
|
||||
val hasOidcCompatibilityFlow: Boolean
|
||||
) : LoginAction()
|
||||
|
||||
data class PostViewEvent(val viewEvent: LoginViewEvents) : LoginAction()
|
||||
|
|
|
@ -45,8 +45,8 @@ import im.vector.app.features.login.terms.LoginTermsFragment
|
|||
import im.vector.app.features.login.terms.LoginTermsFragmentArgument
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||
import im.vector.app.features.pin.UnlockedActivity
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import im.vector.lib.core.utils.compat.getParcelableExtraCompat
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
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.toLocalizedLoginTerms
|
||||
|
|
|
@ -39,7 +39,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixError
|
||||
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||
|
@ -202,11 +201,11 @@ class LoginFragment :
|
|||
|
||||
if (state.loginMode is LoginMode.SsoAndPassword) {
|
||||
views.loginSocialLoginContainer.isVisible = true
|
||||
views.loginSocialLoginButtons.render(state.loginMode.ssoState, ssoMode(state)) { provider ->
|
||||
views.loginSocialLoginButtons.render(state.loginMode, ssoMode(state)) { provider ->
|
||||
loginViewModel.getSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = state.deviceId,
|
||||
providerId = provider?.id
|
||||
providerId = provider?.id,
|
||||
action = if (state.signMode == SignMode.SignUp) SSOAction.REGISTER else SSOAction.LOGIN
|
||||
)
|
||||
?.let { openInCustomTab(it) }
|
||||
|
|
|
@ -23,8 +23,8 @@ sealed class LoginMode : Parcelable { // Parcelable because persist state
|
|||
|
||||
@Parcelize object Unknown : LoginMode()
|
||||
@Parcelize object Password : LoginMode()
|
||||
@Parcelize data class Sso(val ssoState: SsoState) : LoginMode()
|
||||
@Parcelize data class SsoAndPassword(val ssoState: SsoState) : LoginMode()
|
||||
@Parcelize data class Sso(val ssoState: SsoState, val hasOidcCompatibilityFlow: Boolean) : LoginMode()
|
||||
@Parcelize data class SsoAndPassword(val ssoState: SsoState, val hasOidcCompatibilityFlow: Boolean) : LoginMode()
|
||||
@Parcelize object Unsupported : LoginMode()
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,8 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.toReducedUrl
|
||||
import im.vector.app.databinding.FragmentLoginSignupSigninSelectionBinding
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
import javax.inject.Inject
|
||||
import im.vector.app.features.login.SocialLoginButtonsView.Mode
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
|
||||
/**
|
||||
* In this screen, the user is asked to sign up or to sign in to the homeserver.
|
||||
|
@ -78,11 +76,11 @@ class LoginSignUpSignInSelectionFragment :
|
|||
when (state.loginMode) {
|
||||
is LoginMode.SsoAndPassword -> {
|
||||
views.loginSignupSigninSignInSocialLoginContainer.isVisible = true
|
||||
views.loginSignupSigninSocialLoginButtons.render(state.loginMode.ssoState(), Mode.MODE_CONTINUE) { provider ->
|
||||
views.loginSignupSigninSocialLoginButtons.render(state.loginMode, Mode.MODE_CONTINUE) { provider ->
|
||||
loginViewModel.getSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = state.deviceId,
|
||||
providerId = provider?.id
|
||||
providerId = provider?.id,
|
||||
action = if (state.signMode == SignMode.SignUp) SSOAction.REGISTER else SSOAction.LOGIN
|
||||
)
|
||||
?.let { openInCustomTab(it) }
|
||||
|
|
|
@ -225,7 +225,7 @@ class LoginViewModel @AssistedInject constructor(
|
|||
setState {
|
||||
copy(
|
||||
signMode = SignMode.SignIn,
|
||||
loginMode = LoginMode.Sso(action.ssoIdentityProviders.toSsoState()),
|
||||
loginMode = LoginMode.Sso(action.ssoIdentityProviders.toSsoState(), action.hasOidcCompatibilityFlow),
|
||||
homeServerUrlFromUser = action.homeServerUrl,
|
||||
homeServerUrl = action.homeServerUrl,
|
||||
deviceId = action.deviceId
|
||||
|
@ -818,8 +818,8 @@ class LoginViewModel @AssistedInject constructor(
|
|||
val loginMode = when {
|
||||
// SSO login is taken first
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) &&
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders.toSsoState())
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders.toSsoState())
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders.toSsoState(), data.hasOidcCompatibilityFlow)
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders.toSsoState(), data.hasOidcCompatibilityFlow)
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
||||
else -> LoginMode.Unsupported
|
||||
}
|
||||
|
|
|
@ -56,6 +56,14 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
|
|||
}
|
||||
}
|
||||
|
||||
var hasOidcCompatibilityFlow: Boolean = false
|
||||
set(value) {
|
||||
if (value != hasOidcCompatibilityFlow) {
|
||||
field = value
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
var listener: InteractionListener? = null
|
||||
|
||||
private fun update() {
|
||||
|
@ -70,7 +78,7 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
|
|||
transformationMethod = null
|
||||
textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
}.let {
|
||||
it.text = getButtonTitle(context.getString(R.string.login_social_sso))
|
||||
it.text = if (hasOidcCompatibilityFlow) context.getString(R.string.login_continue) else getButtonTitle(context.getString(R.string.login_social_sso))
|
||||
it.textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
it.setOnClickListener {
|
||||
listener?.onProviderSelected(null)
|
||||
|
@ -160,11 +168,13 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
|
|||
}
|
||||
}
|
||||
|
||||
fun SocialLoginButtonsView.render(state: SsoState, mode: SocialLoginButtonsView.Mode, listener: (SsoIdentityProvider?) -> Unit) {
|
||||
fun SocialLoginButtonsView.render(loginMode: LoginMode, mode: SocialLoginButtonsView.Mode, listener: (SsoIdentityProvider?) -> Unit) {
|
||||
this.mode = mode
|
||||
val state = loginMode.ssoState()
|
||||
this.ssoIdentityProviders = when (state) {
|
||||
SsoState.Fallback -> null
|
||||
is SsoState.IdentityProviders -> state.providers.sorted()
|
||||
}
|
||||
this.hasOidcCompatibilityFlow = (loginMode is LoginMode.Sso && loginMode.hasOidcCompatibilityFlow) || (loginMode is LoginMode.SsoAndPassword && loginMode.hasOidcCompatibilityFlow)
|
||||
this.listener = SocialLoginButtonsView.InteractionListener { listener(it) }
|
||||
}
|
||||
|
|
|
@ -48,13 +48,13 @@ class StartAuthenticationFlowUseCase @Inject constructor(
|
|||
preferredLoginMode = preferredLoginMode,
|
||||
supportedLoginTypes = authFlow.supportedLoginTypes,
|
||||
hasOidcCompatibilityFlow = authFlow.hasOidcCompatibilityFlow,
|
||||
isLogoutDevicesSupported = authFlow.isLogoutDevicesSupported
|
||||
isLoginWithQrSupported = authFlow.isLoginWithQrSupported,
|
||||
isLogoutDevicesSupported = authFlow.isLogoutDevicesSupported,
|
||||
isLoginWithQrSupported = authFlow.isLoginWithQrSupported
|
||||
)
|
||||
|
||||
private fun LoginFlowResult.findPreferredLoginMode() = when {
|
||||
supportedLoginTypes.containsAllItems(LoginFlowTypes.SSO, LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(ssoIdentityProviders.toSsoState())
|
||||
supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(ssoIdentityProviders.toSsoState())
|
||||
supportedLoginTypes.containsAllItems(LoginFlowTypes.SSO, LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(ssoIdentityProviders.toSsoState(), hasOidcCompatibilityFlow)
|
||||
supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(ssoIdentityProviders.toSsoState(), hasOidcCompatibilityFlow)
|
||||
supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
||||
else -> LoginMode.Unsupported
|
||||
}
|
||||
|
|
|
@ -26,10 +26,9 @@ import com.airbnb.mvrx.withState
|
|||
import im.vector.app.core.utils.openUrlInChromeCustomTab
|
||||
import im.vector.app.features.login.SSORedirectRouterActivity
|
||||
import im.vector.app.features.login.hasSso
|
||||
import im.vector.app.features.login.ssoIdentityProviders
|
||||
import im.vector.app.features.login.ssoState
|
||||
import im.vector.app.features.onboarding.OnboardingFlow
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import im.vector.app.features.login.ssoState
|
||||
|
||||
abstract class AbstractSSOFtueAuthFragment<VB : ViewBinding> : AbstractFtueAuthFragment<VB>() {
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ import im.vector.app.features.VectorFeatures
|
|||
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.SsoState
|
||||
import im.vector.app.features.login.qr.QrCodeLoginArgs
|
||||
import im.vector.app.features.login.qr.QrCodeLoginType
|
||||
import im.vector.app.features.login.render
|
||||
|
@ -50,7 +49,6 @@ import im.vector.app.features.onboarding.OnboardingViewState
|
|||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
import reactivecircus.flowbinding.android.widget.textChanges
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -154,11 +152,11 @@ class FtueAuthCombinedLoginFragment :
|
|||
when (state.selectedHomeserver.preferredLoginMode) {
|
||||
is LoginMode.SsoAndPassword -> {
|
||||
showUsernamePassword()
|
||||
renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoState)
|
||||
renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode)
|
||||
}
|
||||
is LoginMode.Sso -> {
|
||||
hideUsernamePassword()
|
||||
renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoState)
|
||||
renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode)
|
||||
}
|
||||
else -> {
|
||||
showUsernamePassword()
|
||||
|
@ -167,10 +165,10 @@ class FtueAuthCombinedLoginFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun renderSsoProviders(deviceId: String?, ssoState: SsoState) {
|
||||
private fun renderSsoProviders(deviceId: String?, loginMode: LoginMode) {
|
||||
views.ssoGroup.isVisible = true
|
||||
views.ssoButtonsHeader.isVisible = isUsernameAndPasswordVisible()
|
||||
views.ssoButtons.render(ssoState, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
|
||||
views.ssoButtons.render(loginMode, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
|
||||
viewModel.fetchSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = deviceId,
|
||||
|
|
|
@ -45,7 +45,6 @@ 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.SsoState
|
||||
import im.vector.app.features.login.render
|
||||
import im.vector.app.features.onboarding.OnboardingAction
|
||||
import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
|
||||
|
@ -54,7 +53,6 @@ import im.vector.app.features.onboarding.OnboardingViewState
|
|||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
|
||||
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||
import org.matrix.android.sdk.api.failure.isInvalidUsername
|
||||
|
@ -209,14 +207,14 @@ class FtueAuthCombinedRegisterFragment :
|
|||
}
|
||||
|
||||
when (state.selectedHomeserver.preferredLoginMode) {
|
||||
is LoginMode.SsoAndPassword -> renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoState)
|
||||
is LoginMode.SsoAndPassword -> renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode)
|
||||
else -> hideSsoProviders()
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderSsoProviders(deviceId: String?, ssoState: SsoState) {
|
||||
private fun renderSsoProviders(deviceId: String?, loginMode: LoginMode) {
|
||||
views.ssoGroup.isVisible = true
|
||||
views.ssoButtons.render(ssoState, SocialLoginButtonsView.Mode.MODE_CONTINUE) { provider ->
|
||||
views.ssoButtons.render(loginMode, SocialLoginButtonsView.Mode.MODE_CONTINUE) { provider ->
|
||||
viewModel.fetchSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = deviceId,
|
||||
|
|
|
@ -48,7 +48,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
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
|
||||
import org.matrix.android.sdk.api.failure.isLoginEmailUnknown
|
||||
|
@ -217,11 +216,11 @@ class FtueAuthLoginFragment :
|
|||
|
||||
if (state.selectedHomeserver.preferredLoginMode is LoginMode.SsoAndPassword) {
|
||||
views.loginSocialLoginContainer.isVisible = true
|
||||
views.loginSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode.ssoState, ssoMode(state)) { provider ->
|
||||
views.loginSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode, ssoMode(state)) { provider ->
|
||||
viewModel.fetchSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = state.deviceId,
|
||||
provider = provider
|
||||
provider = provider,
|
||||
action = if (state.signMode == SignMode.SignUp) SSOAction.REGISTER else SSOAction.LOGIN
|
||||
)
|
||||
?.let { openInCustomTab(it) }
|
||||
|
|
|
@ -37,8 +37,6 @@ import im.vector.app.features.onboarding.OnboardingAction
|
|||
import im.vector.app.features.onboarding.OnboardingFlow
|
||||
import im.vector.app.features.onboarding.OnboardingViewState
|
||||
import org.matrix.android.sdk.api.auth.SSOAction
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen, the user is asked to sign up or to sign in to the homeserver.
|
||||
|
@ -85,11 +83,11 @@ class FtueAuthSignUpSignInSelectionFragment :
|
|||
when (state.selectedHomeserver.preferredLoginMode) {
|
||||
is LoginMode.SsoAndPassword -> {
|
||||
views.loginSignupSigninSignInSocialLoginContainer.isVisible = true
|
||||
views.loginSignupSigninSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode.ssoState, Mode.MODE_CONTINUE) { provider ->
|
||||
views.loginSignupSigninSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode, Mode.MODE_CONTINUE) { provider ->
|
||||
viewModel.fetchSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = state.deviceId,
|
||||
provider = provider
|
||||
provider = provider,
|
||||
action = if (state.signMode == SignMode.SignUp) SSOAction.REGISTER else SSOAction.LOGIN
|
||||
)
|
||||
?.let { openInCustomTab(it) }
|
||||
|
|
|
@ -58,6 +58,7 @@ class VectorPreferences @Inject constructor(
|
|||
const val SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY = "SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY"
|
||||
const val SETTINGS_DISCOVERY_PREFERENCE_KEY = "SETTINGS_DISCOVERY_PREFERENCE_KEY"
|
||||
const val SETTINGS_EMAILS_AND_PHONE_NUMBERS_PREFERENCE_KEY = "SETTINGS_EMAILS_AND_PHONE_NUMBERS_PREFERENCE_KEY"
|
||||
const val SETTINGS_EXTERNAL_ACCOUNT_MANAGEMENT_KEY = "SETTINGS_EXTERNAL_ACCOUNT_MANAGEMENT_KEY"
|
||||
|
||||
const val SETTINGS_CLEAR_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_CACHE_PREFERENCE_KEY"
|
||||
const val SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY = "SETTINGS_CLEAR_MEDIA_CACHE_PREFERENCE_KEY"
|
||||
|
|
|
@ -48,6 +48,7 @@ import im.vector.app.core.preference.VectorPreference
|
|||
import im.vector.app.core.preference.VectorSwitchPreference
|
||||
import im.vector.app.core.utils.TextUtils
|
||||
import im.vector.app.core.utils.getSizeOfFiles
|
||||
import im.vector.app.core.utils.openUrlInExternalBrowser
|
||||
import im.vector.app.core.utils.toast
|
||||
import im.vector.app.databinding.DialogChangePasswordBinding
|
||||
import im.vector.app.features.MainActivity
|
||||
|
@ -71,6 +72,7 @@ import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerS
|
|||
import org.matrix.android.sdk.flow.flow
|
||||
import org.matrix.android.sdk.flow.unwrap
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -101,6 +103,9 @@ class VectorSettingsGeneralFragment :
|
|||
private val mIdentityServerPreference by lazy {
|
||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_IDENTITY_SERVER_PREFERENCE_KEY)!!
|
||||
}
|
||||
private val mExternalAccountManagementPreference by lazy {
|
||||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_EXTERNAL_ACCOUNT_MANAGEMENT_KEY)!!
|
||||
}
|
||||
|
||||
// Local contacts
|
||||
private val mContactSettingsCategory by lazy {
|
||||
|
@ -204,6 +209,24 @@ class VectorSettingsGeneralFragment :
|
|||
|
||||
mIdentityServerPreference.onPreferenceClickListener = openDiscoveryScreenPreferenceClickListener
|
||||
|
||||
// External account management URL for delegated OIDC auth
|
||||
// Hide the preference if no URL is given by server
|
||||
if (homeServerCapabilities.externalAccountManagementUrl != null) {
|
||||
mExternalAccountManagementPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
openUrlInExternalBrowser(it.context, homeServerCapabilities.externalAccountManagementUrl)
|
||||
true
|
||||
}
|
||||
|
||||
val hostname = URL(homeServerCapabilities.externalAccountManagementUrl).host
|
||||
|
||||
mExternalAccountManagementPreference.summary = requireContext().getString(
|
||||
R.string.settings_external_account_management,
|
||||
hostname
|
||||
)
|
||||
} else {
|
||||
mExternalAccountManagementPreference.isVisible = false
|
||||
}
|
||||
|
||||
// Advanced settings
|
||||
|
||||
// user account
|
||||
|
|
|
@ -66,7 +66,8 @@ class SoftLogoutFragment :
|
|||
LoginAction.SetupSsoForSessionRecovery(
|
||||
softLogoutViewState.homeServerUrl,
|
||||
softLogoutViewState.deviceId,
|
||||
mode.ssoState.providersOrNull()
|
||||
mode.ssoState.providersOrNull(),
|
||||
mode.hasOidcCompatibilityFlow
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -75,7 +76,8 @@ class SoftLogoutFragment :
|
|||
LoginAction.SetupSsoForSessionRecovery(
|
||||
softLogoutViewState.homeServerUrl,
|
||||
softLogoutViewState.deviceId,
|
||||
mode.ssoState.providersOrNull()
|
||||
mode.ssoState.providersOrNull(),
|
||||
mode.hasOidcCompatibilityFlow
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -85,7 +87,8 @@ class SoftLogoutFragment :
|
|||
LoginAction.SetupSsoForSessionRecovery(
|
||||
softLogoutViewState.homeServerUrl,
|
||||
softLogoutViewState.deviceId,
|
||||
null
|
||||
null,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -118,8 +118,11 @@ class SoftLogoutViewModel @AssistedInject constructor(
|
|||
val loginMode = when {
|
||||
// SSO login is taken first
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) &&
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders.toSsoState())
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders.toSsoState())
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(
|
||||
data.ssoIdentityProviders.toSsoState(),
|
||||
data.hasOidcCompatibilityFlow
|
||||
)
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders.toSsoState(), data.hasOidcCompatibilityFlow)
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
||||
else -> LoginMode.Unsupported
|
||||
}
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
android:summary="@string/settings_discovery_manage"
|
||||
android:title="@string/settings_discovery_category" />
|
||||
|
||||
<im.vector.app.core.preference.VectorPreference
|
||||
android:key="SETTINGS_EXTERNAL_ACCOUNT_MANAGEMENT_KEY"
|
||||
android:persistent="false"
|
||||
android:summary="@string/settings_external_account_management"
|
||||
android:title="@string/settings_external_account_management_title" />
|
||||
|
||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||
|
||||
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||
|
@ -117,4 +123,4 @@
|
|||
|
||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
|
|
@ -98,7 +98,7 @@ class StartAuthenticationFlowUseCaseTest {
|
|||
|
||||
result shouldBeEqualTo expectedResult(
|
||||
supportedLoginTypes = SSO_LOGIN_TYPE,
|
||||
preferredLoginMode = LoginMode.Sso(SsoState.Fallback),
|
||||
preferredLoginMode = LoginMode.Sso(SsoState.Fallback, false),
|
||||
)
|
||||
verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ class StartAuthenticationFlowUseCaseTest {
|
|||
|
||||
result shouldBeEqualTo expectedResult(
|
||||
supportedLoginTypes = SSO_LOGIN_TYPE,
|
||||
preferredLoginMode = LoginMode.Sso(SsoState.IdentityProviders(SSO_IDENTITY_PROVIDERS)),
|
||||
preferredLoginMode = LoginMode.Sso(SsoState.IdentityProviders(SSO_IDENTITY_PROVIDERS), false),
|
||||
)
|
||||
verifyClearsAndThenStartsLogin(A_HOMESERVER_CONFIG)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ fun aHomeServerCapabilities(
|
|||
defaultIdentityServerUrl: String? = null,
|
||||
roomVersions: RoomVersionCapabilities? = null,
|
||||
canRemotelyTogglePushNotificationsOfDevices: Boolean = true,
|
||||
externalAccountManagementUrl: String? = null,
|
||||
) = HomeServerCapabilities(
|
||||
canChangePassword = canChangePassword,
|
||||
canChangeDisplayName = canChangeDisplayName,
|
||||
|
@ -39,4 +40,5 @@ fun aHomeServerCapabilities(
|
|||
defaultIdentityServerUrl = defaultIdentityServerUrl,
|
||||
roomVersions = roomVersions,
|
||||
canRemotelyTogglePushNotificationsOfDevices = canRemotelyTogglePushNotificationsOfDevices,
|
||||
externalAccountManagementUrl = externalAccountManagementUrl,
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue