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,
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? = null,
deviceId: String? = null
): Session
}

View file

@ -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
}
}

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.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
}

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.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 qrLoginTokenTask: QrLoginTokenTask
) : AuthenticationService {
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 {
val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
return retrofit.create(AuthAPI::class.java)

View file

@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
internal interface LoginParams {
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 = "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 {

View file

@ -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? = null,
@Json(name = "device_id") override val deviceId: String? = null
) : 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.DIRECT,
LoginType.CUSTOM,
LoginType.QR,
LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
LoginType.UNKNOWN -> Unit
}