Merge pull request #7358 from vector-im/feature/hughns/qr_code_login_task

This commit is contained in:
Hugh Nimmo-Smith 2022-10-14 09:38:07 +01:00 committed by GitHub
commit 626e3dbd10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 138 additions and 5 deletions

1
changelog.d/7358.sdk Normal file
View File

@ -0,0 +1 @@
Add support for `m.login.token` auth during QR code based sign in

View File

@ -124,4 +124,18 @@ interface AuthenticationService {
initialDeviceName: String, initialDeviceName: String,
deviceId: String? = null deviceId: String? = null
): Session ): Session
/**
* Authenticate using m.login.token method during sign in with QR code.
* @param homeServerConnectionConfig the information about the homeserver and other configuration
* @param loginToken the m.login.token
* @param initialDeviceName the initial device name
* @param deviceId the device id, optional. If not provided or null, the server will generate one.
*/
suspend fun loginUsingQrLoginToken(
homeServerConnectionConfig: HomeServerConnectionConfig,
loginToken: String,
initialDeviceName: String? = null,
deviceId: String? = null
): Session
} }

View File

@ -22,7 +22,8 @@ enum class LoginType {
UNSUPPORTED, UNSUPPORTED,
CUSTOM, CUSTOM,
DIRECT, DIRECT,
UNKNOWN; UNKNOWN,
QR;
companion object { companion object {
@ -32,6 +33,7 @@ enum class LoginType {
UNSUPPORTED.name -> UNSUPPORTED UNSUPPORTED.name -> UNSUPPORTED
CUSTOM.name -> CUSTOM CUSTOM.name -> CUSTOM
DIRECT.name -> DIRECT DIRECT.name -> DIRECT
QR.name -> QR
else -> UNKNOWN else -> UNKNOWN
} }
} }

View File

@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule
import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore
import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore
import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask
import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask
import org.matrix.android.sdk.internal.auth.login.DirectLoginTask import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.database.RealmKeysUtils
import org.matrix.android.sdk.internal.di.AuthDatabase import org.matrix.android.sdk.internal.di.AuthDatabase
import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
@ -94,4 +96,7 @@ internal abstract class AuthModule {
@Binds @Binds
abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
@Binds
abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask
} }

View File

@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
import org.matrix.android.sdk.internal.auth.db.PendingSessionData import org.matrix.android.sdk.internal.auth.db.PendingSessionData
import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard
import org.matrix.android.sdk.internal.auth.login.DirectLoginTask import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.Versions
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
@ -62,7 +63,8 @@ internal class DefaultAuthenticationService @Inject constructor(
private val sessionCreator: SessionCreator, private val sessionCreator: SessionCreator,
private val pendingSessionStore: PendingSessionStore, private val pendingSessionStore: PendingSessionStore,
private val getWellknownTask: GetWellknownTask, private val getWellknownTask: GetWellknownTask,
private val directLoginTask: DirectLoginTask private val directLoginTask: DirectLoginTask,
private val qrLoginTokenTask: QrLoginTokenTask
) : AuthenticationService { ) : AuthenticationService {
private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@ -404,6 +406,22 @@ internal class DefaultAuthenticationService @Inject constructor(
) )
} }
override suspend fun loginUsingQrLoginToken(
homeServerConnectionConfig: HomeServerConnectionConfig,
loginToken: String,
initialDeviceName: String?,
deviceId: String?,
): Session {
return qrLoginTokenTask.execute(
QrLoginTokenTask.Params(
homeServerConnectionConfig = homeServerConnectionConfig,
loginToken = loginToken,
deviceName = initialDeviceName,
deviceId = deviceId
)
)
}
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString()) val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
return retrofit.create(AuthAPI::class.java) return retrofit.create(AuthAPI::class.java)

View File

@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
internal interface LoginParams { internal interface LoginParams {
val type: String val type: String
val deviceDisplayName: String?
val deviceId: String?
} }

View File

@ -30,8 +30,8 @@ internal data class PasswordLoginParams(
@Json(name = "identifier") val identifier: Map<String, String>, @Json(name = "identifier") val identifier: Map<String, String>,
@Json(name = "password") val password: String, @Json(name = "password") val password: String,
@Json(name = "type") override val type: String, @Json(name = "type") override val type: String,
@Json(name = "initial_device_display_name") val deviceDisplayName: String?, @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
@Json(name = "device_id") val deviceId: String? @Json(name = "device_id") override val deviceId: String?
) : LoginParams { ) : LoginParams {
companion object { companion object {

View File

@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class TokenLoginParams( internal data class TokenLoginParams(
@Json(name = "type") override val type: String = LoginFlowTypes.TOKEN, @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
@Json(name = "token") val token: String @Json(name = "token") val token: String,
@Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null,
@Json(name = "device_id") override val deviceId: String? = null
) : LoginParams ) : LoginParams

View File

@ -0,0 +1,88 @@
/*
* Copyright 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.auth.login
import dagger.Lazy
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.auth.LoginType
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.auth.AuthAPI
import org.matrix.android.sdk.internal.auth.SessionCreator
import org.matrix.android.sdk.internal.auth.data.TokenLoginParams
import org.matrix.android.sdk.internal.di.Unauthenticated
import org.matrix.android.sdk.internal.network.RetrofitFactory
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
internal interface QrLoginTokenTask : Task<QrLoginTokenTask.Params, Session> {
data class Params(
val homeServerConnectionConfig: HomeServerConnectionConfig,
val loginToken: String,
val deviceName: String?,
val deviceId: String?
)
}
internal class DefaultQrLoginTokenTask @Inject constructor(
@Unauthenticated
private val okHttpClient: Lazy<OkHttpClient>,
private val retrofitFactory: RetrofitFactory,
private val sessionCreator: SessionCreator,
) : QrLoginTokenTask {
override suspend fun execute(params: QrLoginTokenTask.Params): Session {
val client = buildClient(params.homeServerConnectionConfig)
val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
val authAPI = retrofitFactory.create(client, homeServerUrl)
.create(AuthAPI::class.java)
val loginParams = TokenLoginParams(
token = params.loginToken,
deviceDisplayName = params.deviceName,
deviceId = params.deviceId
)
val credentials = try {
executeRequest(null) {
authAPI.login(loginParams)
}
} catch (throwable: Throwable) {
throw when (throwable) {
is UnrecognizedCertificateException -> Failure.UnrecognizedCertificateFailure(
homeServerUrl,
throwable.fingerprint
)
else -> throwable
}
}
return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.QR)
}
private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
return okHttpClient.get()
.newBuilder()
.addSocketFactory(homeServerConnectionConfig)
.build()
}
}

View File

@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor(
LoginType.SSO -> buildLoginSSOForm() LoginType.SSO -> buildLoginSSOForm()
LoginType.DIRECT, LoginType.DIRECT,
LoginType.CUSTOM, LoginType.CUSTOM,
LoginType.QR,
LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
LoginType.UNKNOWN -> Unit LoginType.UNKNOWN -> Unit
} }