Merge pull request #2568 from vector-im/feature/bma/sso_sdk

Let the SDK compute the URLs
This commit is contained in:
Benoit Marty 2020-12-28 14:20:21 +01:00 committed by GitHub
commit 7ae2b34a9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 136 additions and 74 deletions

View file

@ -41,6 +41,16 @@ interface AuthenticationService {
*/
fun getLoginFlowOfSession(sessionId: String, callback: MatrixCallback<LoginFlowResult>): Cancelable
/**
* Get a SSO url
*/
fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String?
/**
* Get the sign in or sign up fallback URL
*/
fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String?
/**
* Return a LoginWizard, to login to the homeserver. The login flow has to be retrieved first.
*/

View file

@ -0,0 +1,37 @@
/*
* Copyright 2020 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.util
import java.net.URLEncoder
/**
* Append param and value to a Url, using "?" or "&". Value parameter will be encoded
* Return this for chaining purpose
*/
fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
if (contains("?")) {
append("&")
} else {
append("?")
}
append(param)
append("=")
append(URLEncoder.encode(value, "utf-8"))
return this
}

View file

@ -14,25 +14,25 @@
* limitations under the License.
*/
package org.matrix.android.sdk.api.auth
package org.matrix.android.sdk.internal.auth
/**
* Path to use when the client does not supported any or all login flows
* Ref: https://matrix.org/docs/spec/client_server/latest#login-fallback
*/
const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
internal const val LOGIN_FALLBACK_PATH = "/_matrix/static/client/login/"
/**
* Path to use when the client does not supported any or all registration flows
* Not documented
*/
const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
internal const val REGISTER_FALLBACK_PATH = "/_matrix/static/client/register/"
/**
* Path to use when the client want to connect using SSO
* Ref: https://matrix.org/docs/spec/client_server/latest#sso-client-login
*/
const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
internal const val SSO_REDIRECT_PATH = "/_matrix/client/r0/login/sso/redirect"
internal const val MSC2858_SSO_REDIRECT_PATH = "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect"
const val SSO_REDIRECT_URL_PARAM = "redirectUrl"
internal const val SSO_REDIRECT_URL_PARAM = "redirectUrl"

View file

@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.api.util.appendParamToUrl
import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.auth.data.LoginFlowResponse
import org.matrix.android.sdk.internal.auth.data.RiotConfig
@ -99,6 +100,52 @@ internal class DefaultAuthenticationService @Inject constructor(
}
}
override fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
val homeServerUrlBase = getHomeServerUrlBase() ?: return null
return buildString {
append(homeServerUrlBase)
if (providerId != null) {
append(MSC2858_SSO_REDIRECT_PATH)
append("/$providerId")
} else {
append(SSO_REDIRECT_PATH)
}
// Set the redirect url
appendParamToUrl(SSO_REDIRECT_URL_PARAM, redirectUrl)
deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
}
}
override fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
val homeServerUrlBase = getHomeServerUrlBase() ?: return null
return buildString {
append(homeServerUrlBase)
if (forSignIn) {
append(LOGIN_FALLBACK_PATH)
deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
} else {
// For sign up
append(REGISTER_FALLBACK_PATH)
}
}
}
private fun getHomeServerUrlBase(): String? {
return pendingSessionData
?.homeServerConnectionConfig
?.homeServerUri
?.toString()
?.trim { it == '/' }
}
override fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<LoginFlowResult>): Cancelable {
pendingSessionData = null

View file

@ -16,26 +16,6 @@
package im.vector.app.core.extensions
import java.net.URLEncoder
/**
* Append param and value to a Url, using "?" or "&". Value parameter will be encoded
* Return this for chaining purpose
*/
fun StringBuilder.appendParamToUrl(param: String, value: String): StringBuilder {
if (contains("?")) {
append("&")
} else {
append("?")
}
append(param)
append("=")
append(URLEncoder.encode(value, "utf-8"))
return this
}
/**
* Ex: "https://matrix.org/" -> "matrix.org"
*/

View file

@ -87,7 +87,12 @@ abstract class AbstractSSOLoginFragment<VB: ViewBinding> : AbstractLoginFragment
withState(loginViewModel) { state ->
if (state.loginMode.hasSso() && state.loginMode.ssoIdentityProviders().isNullOrEmpty()) {
// in this case we can prefetch (not other cases for privacy concerns)
prefetchUrl(state.getSsoUrl(null))
loginViewModel.getSsoUrl(
redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
deviceId = state.deviceId,
providerId = null
)
?.let { prefetchUrl(it) }
}
}
}

View file

@ -360,6 +360,9 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarCo
private const val EXTRA_CONFIG = "EXTRA_CONFIG"
// Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
const val VECTOR_REDIRECT_URL = "element://connect"
fun newIntent(context: Context, loginConfig: LoginConfig?): Intent {
return Intent(context, LoginActivity::class.java).apply {
putExtra(EXTRA_CONFIG, loginConfig)

View file

@ -193,7 +193,12 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
views.loginSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders
views.loginSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
override fun onProviderSelected(id: String?) {
openInCustomTab(state.getSsoUrl(id))
loginViewModel.getSsoUrl(
redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
deviceId = state.deviceId,
providerId = id
)
?.let { openInCustomTab(it) }
}
}
} else {

View file

@ -76,8 +76,12 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
views.loginSignupSigninSocialLoginButtons.ssoIdentityProviders = state.loginMode.ssoIdentityProviders()
views.loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
override fun onProviderSelected(id: String?) {
val url = withState(loginViewModel) { it.getSsoUrl(id) }
openInCustomTab(url)
loginViewModel.getSsoUrl(
redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
deviceId = state.deviceId,
providerId = id
)
?.let { openInCustomTab(it) }
}
}
}
@ -105,7 +109,12 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLogi
private fun submit() = withState(loginViewModel) { state ->
if (state.loginMode is LoginMode.Sso) {
openInCustomTab(state.getSsoUrl(null))
loginViewModel.getSsoUrl(
redirectUrl = LoginActivity.VECTOR_REDIRECT_URL,
deviceId = state.deviceId,
providerId = null
)
?.let { openInCustomTab(it) }
} else {
loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignUp))
}

View file

@ -818,4 +818,12 @@ class LoginViewModel @AssistedInject constructor(
fun getInitialHomeServerUrl(): String? {
return loginConfig?.homeServerUrl
}
fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
return authenticationService.getSsoUrl(redirectUrl, deviceId, providerId)
}
fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
return authenticationService.getFallbackUrl(forSignIn, deviceId)
}
}

View file

@ -22,10 +22,6 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.PersistState
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.app.core.extensions.appendParamToUrl
import org.matrix.android.sdk.api.auth.MSC2858_SSO_REDIRECT_PATH
import org.matrix.android.sdk.api.auth.SSO_REDIRECT_PATH
import org.matrix.android.sdk.api.auth.SSO_REDIRECT_URL_PARAM
data class LoginViewState(
val asyncLoginAction: Async<Unit> = Uninitialized,
@ -69,27 +65,4 @@ data class LoginViewState(
fun isUserLogged(): Boolean {
return asyncLoginAction is Success
}
fun getSsoUrl(providerId: String?): String {
return buildString {
append(homeServerUrl?.trim { it == '/' })
if (providerId != null) {
append(MSC2858_SSO_REDIRECT_PATH)
append("/$providerId")
} else {
append(SSO_REDIRECT_PATH)
}
// Set a redirect url we will intercept later
appendParamToUrl(SSO_REDIRECT_URL_PARAM, VECTOR_REDIRECT_URL)
deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
}
}
companion object {
// Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
private const val VECTOR_REDIRECT_URL = "element://connect"
}
}

View file

@ -33,14 +33,11 @@ import android.webkit.WebViewClient
import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.activityViewModel
import im.vector.app.R
import im.vector.app.core.extensions.appendParamToUrl
import im.vector.app.core.utils.AssetReader
import im.vector.app.databinding.FragmentLoginWebBinding
import im.vector.app.features.signout.soft.SoftLogoutAction
import im.vector.app.features.signout.soft.SoftLogoutViewModel
import org.matrix.android.sdk.api.auth.LOGIN_FALLBACK_PATH
import org.matrix.android.sdk.api.auth.REGISTER_FALLBACK_PATH
import org.matrix.android.sdk.api.auth.data.Credentials
import org.matrix.android.sdk.internal.di.MoshiProvider
import timber.log.Timber
@ -119,19 +116,7 @@ class LoginWebFragment @Inject constructor(
}
private fun launchWebView(state: LoginViewState) {
val url = buildString {
append(state.homeServerUrl?.trim { it == '/' })
if (state.signMode == SignMode.SignIn) {
append(LOGIN_FALLBACK_PATH)
state.deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
} else {
// MODE_REGISTER
append(REGISTER_FALLBACK_PATH)
}
}
val url = loginViewModel.getFallbackUrl(state.signMode == SignMode.SignIn, state.deviceId) ?: return
views.loginWebWebView.loadUrl(url)