From 8645f74c774fc5ef5a57070a05fc021fb6ba8628 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:11:15 +0100 Subject: [PATCH 1/9] Support for login by m.login.token during QR code sign in --- .../sdk/api/auth/AuthenticationService.kt | 14 +++ .../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, 126 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 5ae70e1978..8f2a784d49 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 @@ -124,4 +124,18 @@ interface AuthenticationService { initialDeviceName: String, deviceId: String? = null ): 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, + 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 446f931847..90dc57b4f0 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 @@ -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.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 @@ -62,7 +63,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() @@ -404,6 +406,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() + } +} From f13419d696c401ee6d00045ec6bb173783fa6d4b Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:18:31 +0100 Subject: [PATCH 2/9] Changelog --- changelog.d/7358.sdk | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7358.sdk diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk new file mode 100644 index 0000000000..3d17076a44 --- /dev/null +++ b/changelog.d/7358.sdk @@ -0,0 +1 @@ +Add support for `m.login.token` auth during QR code based sign in From 09da74b029bfd96b73a173f340dc57c55bdbc950 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:19:39 +0100 Subject: [PATCH 3/9] Linting --- .../sdk/internal/auth/DefaultAuthenticationService.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 90dc57b4f0..7fd730bece 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 @@ -406,7 +406,12 @@ internal class DefaultAuthenticationService @Inject constructor( ) } - override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session { + override suspend fun loginUsingQrLoginToken( + homeServerConnectionConfig: HomeServerConnectionConfig, + loginToken: String, + initialDeviceName: String, + deviceId: String?, + ): Session { return loginTokenAuthTask.execute( QrLoginTokenTask.Params( homeServerConnectionConfig = homeServerConnectionConfig, From 5bff9ceec46d32b983d75230b170e89a34d1eef7 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:35:45 +0100 Subject: [PATCH 4/9] Make initialDeviceName optional --- .../org/matrix/android/sdk/api/auth/AuthenticationService.kt | 2 +- .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 8f2a784d49..c8065e4524 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 @@ -135,7 +135,7 @@ interface AuthenticationService { suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String, + initialDeviceName: String?, deviceId: String? = null ): Session } 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 7fd730bece..6c3622ed5d 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 @@ -409,7 +409,7 @@ internal class DefaultAuthenticationService @Inject constructor( override suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String, + initialDeviceName: String?, deviceId: String?, ): Session { return loginTokenAuthTask.execute( From b5b32b7fdaafd03b4cc60d34903c5d17d6069a5f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:58:19 +0100 Subject: [PATCH 5/9] Use correct var name --- .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6c3622ed5d..5b12e3bdc3 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 @@ -64,7 +64,7 @@ internal class DefaultAuthenticationService @Inject constructor( private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, private val directLoginTask: DirectLoginTask, - private val loginTokenAuthTask: QrLoginTokenTask + private val qrLoginTokenTask: QrLoginTokenTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -412,7 +412,7 @@ internal class DefaultAuthenticationService @Inject constructor( initialDeviceName: String?, deviceId: String?, ): Session { - return loginTokenAuthTask.execute( + return qrLoginTokenTask.execute( QrLoginTokenTask.Params( homeServerConnectionConfig = homeServerConnectionConfig, loginToken = loginToken, From 8c86b3d297b38533b7f38da69e3407fe800a673e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:02:57 +0100 Subject: [PATCH 6/9] Add missing binding --- .../java/org/matrix/android/sdk/internal/auth/AuthModule.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt index 463692e574..b1f65194f1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt @@ -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.RealmSessionParamsStore 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.QrLoginTokenTask import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.AuthDatabase import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter @@ -94,4 +96,7 @@ internal abstract class AuthModule { @Binds abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService + + @Binds + abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask } From c6e7185f8b7c1cdb11f5acf0f11aa17ca761d8bf Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:08:50 +0100 Subject: [PATCH 7/9] Set default value for optional params --- .../matrix/android/sdk/internal/auth/data/TokenLoginParams.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 22cc185fa7..52045a1d7a 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 @@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes internal data class TokenLoginParams( @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN, @Json(name = "token") val token: String, - @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, - @Json(name = "device_id") override val deviceId: String? + @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null, + @Json(name = "device_id") override val deviceId: String? = null ) : LoginParams From 81f21971993686ae4de245b16f70b15e855ff402 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:11:41 +0100 Subject: [PATCH 8/9] Another default value fix --- .../org/matrix/android/sdk/api/auth/AuthenticationService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c8065e4524..e490311b91 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 @@ -135,7 +135,7 @@ interface AuthenticationService { suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String?, + initialDeviceName: String? = null, deviceId: String? = null ): Session } From 738242c8378be68392d03567e5b4714a2591cd20 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:28:47 +0100 Subject: [PATCH 9/9] Map for soft logout --- .../im/vector/app/features/signout/soft/SoftLogoutController.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt index b1a240e942..a1ed27df1d 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor( LoginType.SSO -> buildLoginSSOForm() LoginType.DIRECT, LoginType.CUSTOM, + LoginType.QR, LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() LoginType.UNKNOWN -> Unit }