From 88238c0f04648a7468c2ee33d86068fa79e4fe5f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:11:15 +0100 Subject: [PATCH] Support for login by m.login.token during QR code sign in --- .../sdk/api/auth/AuthenticationService.kt | 13 +++ .../matrix/android/sdk/api/auth/LoginType.kt | 4 +- .../auth/DefaultAuthenticationService.kt | 15 +++- .../sdk/internal/auth/data/LoginParams.kt | 2 + .../internal/auth/data/PasswordLoginParams.kt | 4 +- .../internal/auth/data/TokenLoginParams.kt | 4 +- .../internal/auth/login/QrLoginTokenTask.kt | 88 +++++++++++++++++++ 7 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt index d040f9c67b..6bb47dda5b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -130,4 +130,17 @@ interface AuthenticationService { * Return true if qr code login is supported by the server, false otherwise. */ suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean + * 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, + deviceId: String? = null + ): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt index 627a825679..991b7b654d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt @@ -22,7 +22,8 @@ enum class LoginType { UNSUPPORTED, CUSTOM, DIRECT, - UNKNOWN; + UNKNOWN, + QR; companion object { @@ -32,6 +33,7 @@ enum class LoginType { UNSUPPORTED.name -> UNSUPPORTED CUSTOM.name -> CUSTOM DIRECT.name -> DIRECT + QR.name -> QR else -> UNKNOWN } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 4f45f807df..5b6f0686c4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -40,6 +40,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.login.DefaultLoginWizard 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.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices @@ -64,7 +65,8 @@ internal class DefaultAuthenticationService @Inject constructor( private val sessionCreator: SessionCreator, private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, - private val directLoginTask: DirectLoginTask + private val directLoginTask: DirectLoginTask, + private val loginTokenAuthTask: QrLoginTokenTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -420,6 +422,17 @@ internal class DefaultAuthenticationService @Inject constructor( } } + override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session { + return loginTokenAuthTask.execute( + QrLoginTokenTask.Params( + homeServerConnectionConfig = homeServerConnectionConfig, + loginToken = loginToken, + deviceName = initialDeviceName, + deviceId = deviceId + ) + ) + } + private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString()) return retrofit.create(AuthAPI::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt index ea8578ed8c..8646752083 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt @@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data internal interface LoginParams { val type: String + val deviceDisplayName: String? + val deviceId: String? } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt index 5f0a2298cb..062b2466e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt @@ -30,8 +30,8 @@ internal data class PasswordLoginParams( @Json(name = "identifier") val identifier: Map, @Json(name = "password") val password: String, @Json(name = "type") override val type: String, - @Json(name = "initial_device_display_name") val deviceDisplayName: String?, - @Json(name = "device_id") val deviceId: String? + @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, + @Json(name = "device_id") override val deviceId: String? ) : LoginParams { companion object { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt index 0c6fb45e78..22cc185fa7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt @@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes @JsonClass(generateAdapter = true) internal data class TokenLoginParams( @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?, + @Json(name = "device_id") override val deviceId: String? ) : LoginParams diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt new file mode 100644 index 0000000000..477719f607 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt @@ -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 { + 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, + 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() + } +}