Skip to content

Commit

Permalink
SSO login is now performed in the default browser (#1400) - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
bmarty committed Jun 4, 2020
1 parent ae7a52c commit 2b23da8
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Improvements 🙌:
- Hide "X made no changes" event by default in timeline (#1430)
- Hide left rooms in breadcrumbs (#766)
- Correctly handle SSO login redirection
- SSO login is now performed in the default browser (#1400)

Bugfix 🐛:
- Switch theme is not fully taken into account without restarting the app
Expand Down
14 changes: 13 additions & 1 deletion vector/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,19 @@
<activity android:name=".features.home.HomeActivity" />
<activity
android:name=".features.login.LoginActivity"
android:windowSoftInputMode="adjustResize" />
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<!-- Add intent filter to handle redirection URL after SSO login in external browser -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="riotx" />
<data android:host="riotx" />
</intent-filter>
</activity>
<activity android:name=".features.media.ImageMediaViewerActivity" />
<activity android:name=".features.media.BigImageViewerActivity" />
<activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,21 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import com.airbnb.mvrx.viewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.auth.SSO_FALLBACK_PATH
import im.vector.matrix.android.api.auth.SSO_REDIRECT_URL_PARAM
import im.vector.matrix.android.api.auth.registration.FlowResult
import im.vector.matrix.android.api.auth.registration.Stage
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.extensions.POP_BACK_STACK_EXCLUSIVE
import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.extensions.addFragmentToBackstack
import im.vector.riotx.core.extensions.appendParamToUrl
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.utils.openUrlInExternalBrowser
import im.vector.riotx.features.home.HomeActivity
import im.vector.riotx.features.login.terms.LoginTermsFragment
import im.vector.riotx.features.login.terms.LoginTermsFragmentArgument
Expand Down Expand Up @@ -244,9 +249,7 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
LoginFragment::class.java,
tag = FRAGMENT_LOGIN_TAG,
option = commonOption)
LoginMode.Sso -> addFragmentToBackstack(R.id.loginFragmentContainer,
LoginWebFragment::class.java,
option = commonOption)
LoginMode.Sso -> performSso(state)
LoginMode.Unsupported -> onLoginModeNotSupported(state.loginModeSupportedTypes)
}
}
Expand All @@ -257,6 +260,35 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
}.exhaustive
}

/**
* Open the external browser...
*/
private fun performSso(state: LoginViewState) {
val url = buildString {
append(state.homeServerUrl?.trim { it == '/' })
append(SSO_FALLBACK_PATH)
// Set a redirect url we will intercept later
appendParamToUrl(SSO_REDIRECT_URL_PARAM, REDIRECT_URL)
state.deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
}
}

openUrlInExternalBrowser(this, url)
}

/**
* ... and handle the redirection here
*/
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)

intent?.data
?.let { tryThis { it.getQueryParameter("loginToken") } }
?.let { loginViewModel.handle(LoginAction.LoginWithToken(it)) }
}

private fun onRegistrationStageNotSupported() {
AlertDialog.Builder(this)
.setTitle(R.string.app_name)
Expand Down Expand Up @@ -339,6 +371,9 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {

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
private const val REDIRECT_URL = "riotx://riotx"

fun newIntent(context: Context, loginConfig: LoginConfig?): Intent {
return Intent(context, LoginActivity::class.java).apply {
putExtra(EXTRA_CONFIG, loginConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ package im.vector.riotx.features.login
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Bitmap
import android.net.Uri
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
Expand All @@ -34,10 +33,7 @@ import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.activityViewModel
import im.vector.matrix.android.api.auth.LOGIN_FALLBACK_PATH
import im.vector.matrix.android.api.auth.REGISTER_FALLBACK_PATH
import im.vector.matrix.android.api.auth.SSO_FALLBACK_PATH
import im.vector.matrix.android.api.auth.SSO_REDIRECT_URL_PARAM
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.riotx.R
import im.vector.riotx.core.extensions.appendParamToUrl
Expand All @@ -50,18 +46,13 @@ import java.net.URLDecoder
import javax.inject.Inject

/**
* This screen is displayed for SSO login and also when the application does not support login flow or registration flow
* This screen is displayed when the application does not support login flow or registration flow
* of the homeserver, as a fallback to login or to create an account
*/
class LoginWebFragment @Inject constructor(
private val assetReader: AssetReader
) : AbstractLoginFragment() {

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 REDIRECT_URL = "riotx://riotx"
}

override fun getLayoutResId() = R.layout.fragment_login_web

private var isWebViewLoaded = false
Expand Down Expand Up @@ -135,13 +126,7 @@ class LoginWebFragment @Inject constructor(
val url = buildString {
append(state.homeServerUrl?.trim { it == '/' })
if (state.signMode == SignMode.SignIn) {
if (state.loginMode == LoginMode.Sso) {
append(SSO_FALLBACK_PATH)
// Set a redirect url we will intercept later
appendParamToUrl(SSO_REDIRECT_URL_PARAM, REDIRECT_URL)
} else {
append(LOGIN_FALLBACK_PATH)
}
append(LOGIN_FALLBACK_PATH)
state.deviceId?.takeIf { it.isNotBlank() }?.let {
// But https://github.com/matrix-org/synapse/issues/5755
appendParamToUrl("device_id", it)
Expand Down Expand Up @@ -261,23 +246,13 @@ class LoginWebFragment @Inject constructor(
}
}
return true
} else if (url.startsWith(REDIRECT_URL)) {
return handleSsoLoginSuccess(url)
}

return super.shouldOverrideUrlLoading(view, url)
}
}
}

private fun handleSsoLoginSuccess(url: String): Boolean {
val uri = Uri.parse(url)
val loginToken = tryThis { uri.getQueryParameter("loginToken") } ?: return false

loginViewModel.handle(LoginAction.LoginWithToken(loginToken))
return true
}

private fun notifyViewModel(credentials: Credentials) {
if (isForSessionRecovery) {
val softLogoutViewModel: SoftLogoutViewModel by activityViewModel()
Expand Down

0 comments on commit 2b23da8

Please sign in to comment.