mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Correctly handle SSO login redirection
This commit is contained in:
parent
2e244dd448
commit
ae7a52cecf
@ -9,6 +9,7 @@ Improvements 🙌:
|
||||
- New wording for notice when current user is the sender
|
||||
- Hide "X made no changes" event by default in timeline (#1430)
|
||||
- Hide left rooms in breadcrumbs (#766)
|
||||
- Correctly handle SSO login redirection
|
||||
|
||||
Bugfix 🐛:
|
||||
- Switch theme is not fully taken into account without restarting the app
|
||||
|
@ -34,6 +34,12 @@ interface LoginWizard {
|
||||
deviceName: String,
|
||||
callback: MatrixCallback<Session>): Cancelable
|
||||
|
||||
/**
|
||||
* Exchange a login token to an access token
|
||||
*/
|
||||
fun loginWithToken(loginToken: String,
|
||||
callback: MatrixCallback<Session>): Cancelable
|
||||
|
||||
/**
|
||||
* Reset user password
|
||||
*/
|
||||
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.auth.data.Versions
|
||||
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.data.RiotConfig
|
||||
import im.vector.matrix.android.internal.auth.data.TokenLoginParams
|
||||
import im.vector.matrix.android.internal.auth.login.ResetPasswordMailConfirmed
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
|
||||
@ -91,6 +92,11 @@ internal interface AuthAPI {
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
||||
fun login(@Body loginParams: PasswordLoginParams): Call<Credentials>
|
||||
|
||||
// Unfortunately we cannot use interface for @Body parameter, so I duplicate the method for the type TokenLoginParams
|
||||
@Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000")
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
||||
fun login(@Body loginParams: TokenLoginParams): Call<Credentials>
|
||||
|
||||
/**
|
||||
* Ask the homeserver to reset the password associated with the provided email.
|
||||
*/
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2020 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.data
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.util.UUID
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class TokenLoginParams(
|
||||
@Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
|
||||
@Json(name = "token") val token: String,
|
||||
// client generated nonce
|
||||
@Json(name = "txn_id") val txId: String = UUID.randomUUID().toString()
|
||||
// Param session is not useful in this case?
|
||||
) : LoginParams
|
@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.auth.PendingSessionStore
|
||||
import im.vector.matrix.android.internal.auth.SessionCreator
|
||||
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||
import im.vector.matrix.android.internal.auth.data.ThreePidMedium
|
||||
import im.vector.matrix.android.internal.auth.data.TokenLoginParams
|
||||
import im.vector.matrix.android.internal.auth.db.PendingSessionData
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
|
||||
@ -65,6 +66,22 @@ internal class DefaultLoginWizard(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#handling-the-authentication-endpoint
|
||||
*/
|
||||
override fun loginWithToken(loginToken: String, callback: MatrixCallback<Session>): Cancelable {
|
||||
return coroutineScope.launchToCallback(coroutineDispatchers.main, callback) {
|
||||
val loginParams = TokenLoginParams(
|
||||
token = loginToken
|
||||
)
|
||||
val credentials = executeRequest<Credentials>(null) {
|
||||
apiCall = authAPI.login(loginParams)
|
||||
}
|
||||
|
||||
sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loginInternal(login: String,
|
||||
password: String,
|
||||
deviceName: String) = withContext(coroutineDispatchers.computation) {
|
||||
|
@ -24,6 +24,7 @@ sealed class LoginAction : VectorViewModelAction {
|
||||
data class UpdateServerType(val serverType: ServerType) : LoginAction()
|
||||
data class UpdateHomeServer(val homeServerUrl: String) : LoginAction()
|
||||
data class UpdateSignMode(val signMode: SignMode) : LoginAction()
|
||||
data class LoginWithToken(val loginToken: String) : LoginAction()
|
||||
data class WebLoginSuccess(val credentials: Credentials) : LoginAction()
|
||||
data class InitWith(val loginConfig: LoginConfig) : LoginAction()
|
||||
data class ResetPassword(val email: String, val newPassword: String) : LoginAction()
|
||||
|
@ -110,6 +110,7 @@ class LoginViewModel @AssistedInject constructor(
|
||||
is LoginAction.InitWith -> handleInitWith(action)
|
||||
is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action)
|
||||
is LoginAction.LoginOrRegister -> handleLoginOrRegister(action)
|
||||
is LoginAction.LoginWithToken -> handleLoginWithToken(action)
|
||||
is LoginAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||
is LoginAction.ResetPassword -> handleResetPassword(action)
|
||||
is LoginAction.ResetPasswordMailConfirmed -> handleResetPasswordMailConfirmed()
|
||||
@ -120,6 +121,41 @@ class LoginViewModel @AssistedInject constructor(
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleLoginWithToken(action: LoginAction.LoginWithToken) {
|
||||
val safeLoginWizard = loginWizard
|
||||
|
||||
if (safeLoginWizard == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentTask = safeLoginWizard.loginWithToken(
|
||||
action.loginToken,
|
||||
object : MatrixCallback<Session> {
|
||||
override fun onSuccess(data: Session) {
|
||||
onSessionCreated(data)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
_viewEvents.post(LoginViewEvents.Failure(failure))
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetupSsoForSessionRecovery(action: LoginAction.SetupSsoForSessionRecovery) {
|
||||
setState {
|
||||
copy(
|
||||
|
@ -21,6 +21,7 @@ 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
|
||||
@ -36,6 +37,7 @@ 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
|
||||
@ -55,6 +57,11 @@ 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
|
||||
@ -130,12 +137,8 @@ class LoginWebFragment @Inject constructor(
|
||||
if (state.signMode == SignMode.SignIn) {
|
||||
if (state.loginMode == LoginMode.Sso) {
|
||||
append(SSO_FALLBACK_PATH)
|
||||
// We do not want to deal with the result, so let the fallback login page to handle it for us
|
||||
appendParamToUrl(SSO_REDIRECT_URL_PARAM,
|
||||
buildString {
|
||||
append(state.homeServerUrl?.trim { it == '/' })
|
||||
append(LOGIN_FALLBACK_PATH)
|
||||
})
|
||||
// Set a redirect url we will intercept later
|
||||
appendParamToUrl(SSO_REDIRECT_URL_PARAM, REDIRECT_URL)
|
||||
} else {
|
||||
append(LOGIN_FALLBACK_PATH)
|
||||
}
|
||||
@ -226,7 +229,9 @@ class LoginWebFragment @Inject constructor(
|
||||
* @return
|
||||
*/
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
|
||||
if (null != url && url.startsWith("js:")) {
|
||||
if (url == null) return super.shouldOverrideUrlLoading(view, url as String?)
|
||||
|
||||
if (url.startsWith("js:")) {
|
||||
var json = url.substring(3)
|
||||
var javascriptResponse: JavascriptResponse? = null
|
||||
|
||||
@ -256,6 +261,8 @@ class LoginWebFragment @Inject constructor(
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else if (url.startsWith(REDIRECT_URL)) {
|
||||
return handleSsoLoginSuccess(url)
|
||||
}
|
||||
|
||||
return super.shouldOverrideUrlLoading(view, url)
|
||||
@ -263,6 +270,14 @@ class LoginWebFragment @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
Loading…
Reference in New Issue
Block a user