From e1fd79de0265919d4e98e0dfd468c0825b1e3a4d Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 13 Apr 2022 21:04:23 +0100 Subject: [PATCH] adding support for signing in to homeservers without wellknown setup --- README.md | 2 +- .../kotlin/app/dapk/st/login/LoginScreen.kt | 46 ++++++++++++++++--- .../kotlin/app/dapk/st/login/LoginState.kt | 3 +- .../app/dapk/st/login/LoginViewModel.kt | 42 +++++++++++------ .../app/dapk/st/matrix/http/HttpExtensions.kt | 4 +- .../app/dapk/st/matrix/auth/AuthService.kt | 14 ++++-- .../st/matrix/auth/internal/AuthRequest.kt | 4 ++ .../auth/internal/DefaultAuthService.kt | 20 +++++--- .../internal/FetchWellKnownUseCaseImpl.kt | 43 ++++++++++++++--- .../LoginWithUserPasswordServerUseCase.kt | 33 +++++++++++++ ...ase.kt => LoginWithUserPasswordUseCase.kt} | 44 +++++++++++------- .../matrix/auth/internal/RegisterUseCase.kt | 9 ++-- test-harness/src/test/kotlin/SmokeTest.kt | 16 +++++-- .../src/test/kotlin/test/TestMatrix.kt | 6 +-- 14 files changed, 218 insertions(+), 68 deletions(-) create mode 100644 matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordServerUseCase.kt rename matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/{LoginUseCase.kt => LoginWithUserPasswordUseCase.kt} (63%) diff --git a/README.md b/README.md index ef67e06..b399aaa 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ### Feature list -- Login with username/password (homeservers must serve `https://${domain}/.well-known/matrix/client`) +- Login with Matrix ID/Password - Combined Room and DM interface - End to end encryption - Message bubbles, supporting text, replies and edits diff --git a/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt b/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt index 8227b82..30bc60f 100644 --- a/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt +++ b/features/login/src/main/kotlin/app/dapk/st/login/LoginScreen.kt @@ -1,5 +1,6 @@ package app.dapk.st.login +import android.widget.Toast import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -7,6 +8,7 @@ import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material.icons.filled.Web import androidx.compose.material.icons.outlined.Lock import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable @@ -14,6 +16,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.text.font.FontWeight @@ -37,9 +40,10 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { var userName by rememberSaveable { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") } + var serverUrl by rememberSaveable { mutableStateOf("") } val keyboardController = LocalSoftwareKeyboardController.current - when (loginViewModel.state) { + when (val state = loginViewModel.state) { is Error -> { Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Column(horizontalAlignment = Alignment.CenterHorizontally) { @@ -58,7 +62,7 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { CircularProgressIndicator() } } - Idle -> + is Content -> Row { Spacer(modifier = Modifier.weight(0.1f)) Column( @@ -91,7 +95,11 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { keyboardOptions = KeyboardOptions(autoCorrect = false, keyboardType = KeyboardType.Email, imeAction = ImeAction.Next) ) - val canDoLoginAttempt = userName.isNotEmpty() && password.isNotEmpty() + val canDoLoginAttempt = if (state.showServerUrl) { + userName.isNotEmpty() && password.isNotEmpty() && serverUrl.isNotEmpty() + } else { + userName.isNotEmpty() && password.isNotEmpty() + } TextField( modifier = Modifier.fillMaxWidth(), @@ -102,10 +110,13 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { leadingIcon = { Icon(imageVector = Icons.Outlined.Lock, contentDescription = null) }, - keyboardActions = KeyboardActions(onDone = { loginViewModel.login(userName, password) }), + keyboardActions = KeyboardActions( + onDone = { loginViewModel.login(userName, password, serverUrl) }, + onNext = { focusManager.moveFocus(FocusDirection.Down) }, + ), keyboardOptions = KeyboardOptions( autoCorrect = false, - imeAction = ImeAction.Done.takeIf { canDoLoginAttempt } ?: ImeAction.None, + imeAction = ImeAction.Done.takeIf { canDoLoginAttempt } ?: ImeAction.Next.takeIf { state.showServerUrl } ?: ImeAction.None, keyboardType = KeyboardType.Password ), visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(), @@ -117,13 +128,32 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { } ) + if (state.showServerUrl) { + TextField( + modifier = Modifier.fillMaxWidth(), + value = serverUrl, + onValueChange = { serverUrl = it }, + label = { Text("Server URL") }, + singleLine = true, + leadingIcon = { + Icon(imageVector = Icons.Default.Web, contentDescription = null) + }, + keyboardActions = KeyboardActions(onDone = { loginViewModel.login(userName, password, serverUrl) }), + keyboardOptions = KeyboardOptions( + autoCorrect = false, + imeAction = ImeAction.Done.takeIf { canDoLoginAttempt } ?: ImeAction.None, + keyboardType = KeyboardType.Uri + ), + ) + } + Spacer(Modifier.height(4.dp)) Button( modifier = Modifier.fillMaxWidth(), onClick = { keyboardController?.hide() - loginViewModel.login(userName, password) + loginViewModel.login(userName, password, serverUrl) }, enabled = canDoLoginAttempt ) { @@ -137,10 +167,14 @@ fun LoginScreen(loginViewModel: LoginViewModel, onLoggedIn: () -> Unit) { @Composable private fun LoginViewModel.ObserveEvents(onLoggedIn: () -> Unit) { + val context = LocalContext.current StartObserving { this@ObserveEvents.events.launch { when (it) { LoginComplete -> onLoggedIn() + LoginEvent.WellKnownMissing -> { + Toast.makeText(context, "Couldn't find the homeserver, please enter the server URL", Toast.LENGTH_LONG).show() + } } } } diff --git a/features/login/src/main/kotlin/app/dapk/st/login/LoginState.kt b/features/login/src/main/kotlin/app/dapk/st/login/LoginState.kt index 5e0245b..f4d0d24 100644 --- a/features/login/src/main/kotlin/app/dapk/st/login/LoginState.kt +++ b/features/login/src/main/kotlin/app/dapk/st/login/LoginState.kt @@ -2,12 +2,13 @@ package app.dapk.st.login sealed interface LoginScreenState { - object Idle : LoginScreenState + data class Content(val showServerUrl: Boolean) : LoginScreenState object Loading : LoginScreenState data class Error(val cause: Throwable) : LoginScreenState } sealed interface LoginEvent { object LoginComplete : LoginEvent + object WellKnownMissing : LoginEvent } diff --git a/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt b/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt index 2969031..e5cbf31 100644 --- a/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt +++ b/features/login/src/main/kotlin/app/dapk/st/login/LoginViewModel.kt @@ -19,26 +19,34 @@ class LoginViewModel( private val profileService: ProfileService, private val errorTracker: ErrorTracker, ) : DapkViewModel( - initialState = Idle + initialState = Content(showServerUrl = false) ) { - fun login(userName: String, password: String) { + private var previousState: LoginScreenState? = null + + fun login(userName: String, password: String, serverUrl: String?) { state = Loading viewModelScope.launch { - kotlin.runCatching { - logP("login") { - authService.login(userName, password).also { - listOf( - async { firebasePushTokenUseCase.registerCurrentToken() }, - async { preloadMe() }, - ).awaitAll() + logP("login") { + when (val result = authService.login(AuthService.LoginRequest(userName, password, serverUrl.takeIfNotEmpty()))) { + is AuthService.LoginResult.Success -> { + runCatching { + listOf( + async { firebasePushTokenUseCase.registerCurrentToken() }, + async { preloadMe() }, + ).awaitAll() + } + _events.tryEmit(LoginComplete) + } + is AuthService.LoginResult.Error -> { + errorTracker.track(result.cause) + state = Error(result.cause) + } + AuthService.LoginResult.MissingWellKnown -> { + _events.tryEmit(LoginEvent.WellKnownMissing) + state = Content(showServerUrl = true) } } - }.onFailure { - errorTracker.track(it) - state = Error(it) - }.onSuccess { - _events.tryEmit(LoginComplete) } } } @@ -46,6 +54,10 @@ class LoginViewModel( private suspend fun preloadMe() = profileService.me(forceRefresh = false) fun start() { - state = Idle + val showServerUrl = previousState?.let { it is Content && it.showServerUrl } ?: false + state = Content(showServerUrl = showServerUrl) } } + + +private fun String?.takeIfNotEmpty() = this?.takeIf { it.isNotEmpty() } \ No newline at end of file diff --git a/matrix/matrix-http/src/main/kotlin/app/dapk/st/matrix/http/HttpExtensions.kt b/matrix/matrix-http/src/main/kotlin/app/dapk/st/matrix/http/HttpExtensions.kt index 2074396..edf24b3 100644 --- a/matrix/matrix-http/src/main/kotlin/app/dapk/st/matrix/http/HttpExtensions.kt +++ b/matrix/matrix-http/src/main/kotlin/app/dapk/st/matrix/http/HttpExtensions.kt @@ -4,6 +4,6 @@ fun String.ensureTrailingSlash(): String { return if (this.endsWith("/")) this else "$this/" } -fun String.ensureHttps(): String { - return if (this.startsWith("https")) this else "https://$this" +fun String.ensureHttpsIfMissing(): String { + return if (this.startsWith("http")) this else "https://$this" } diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/AuthService.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/AuthService.kt index caa47d2..95db7f4 100644 --- a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/AuthService.kt +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/AuthService.kt @@ -10,16 +10,24 @@ import app.dapk.st.matrix.common.UserCredentials private val SERVICE_KEY = AuthService::class interface AuthService : MatrixService { - suspend fun login(userName: String, password: String): UserCredentials + suspend fun login(request: LoginRequest): LoginResult suspend fun register(userName: String, password: String, homeServer: String): UserCredentials + + + sealed interface LoginResult { + data class Success(val userCredentials: UserCredentials) : LoginResult + object MissingWellKnown : LoginResult + data class Error(val cause: Throwable) : LoginResult + } + + data class LoginRequest(val userName: String, val password: String, val serverUrl: String?) } fun MatrixServiceInstaller.installAuthService( credentialsStore: CredentialsStore, - authConfig: AuthConfig = AuthConfig(), ) { this.install { (httpClient, json) -> - SERVICE_KEY to DefaultAuthService(httpClient, credentialsStore, json, authConfig) + SERVICE_KEY to DefaultAuthService(httpClient, credentialsStore, json) } } diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/AuthRequest.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/AuthRequest.kt index 015a7fa..e8d8ea8 100644 --- a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/AuthRequest.kt +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/AuthRequest.kt @@ -49,6 +49,10 @@ internal fun wellKnownRequest(baseUrl: String) = httpRequest( authenticated = false, ) +@JvmInline +@Serializable +internal value class RawResponse(val value: String) + internal data class Auth( val session: String, val type: String, diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/DefaultAuthService.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/DefaultAuthService.kt index b86eb87..c017b27 100644 --- a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/DefaultAuthService.kt +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/DefaultAuthService.kt @@ -1,25 +1,33 @@ package app.dapk.st.matrix.auth.internal -import app.dapk.st.matrix.auth.AuthConfig import app.dapk.st.matrix.auth.AuthService import app.dapk.st.matrix.common.CredentialsStore +import app.dapk.st.matrix.common.HomeServerUrl import app.dapk.st.matrix.common.UserCredentials import app.dapk.st.matrix.http.MatrixHttpClient +import app.dapk.st.matrix.http.ensureHttpsIfMissing +import app.dapk.st.matrix.http.ensureTrailingSlash import kotlinx.serialization.json.Json internal class DefaultAuthService( httpClient: MatrixHttpClient, credentialsStore: CredentialsStore, json: Json, - authConfig: AuthConfig, ) : AuthService { private val fetchWellKnownUseCase = FetchWellKnownUseCaseImpl(httpClient, json) - private val loginUseCase = LoginUseCase(httpClient, credentialsStore, fetchWellKnownUseCase, authConfig) - private val registerCase = RegisterUseCase(httpClient, credentialsStore, json, fetchWellKnownUseCase, authConfig) + private val loginUseCase = LoginWithUserPasswordUseCase(httpClient, credentialsStore, fetchWellKnownUseCase) + private val loginServerUseCase = LoginWithUserPasswordServerUseCase(httpClient, credentialsStore) + private val registerCase = RegisterUseCase(httpClient, credentialsStore, json, fetchWellKnownUseCase) - override suspend fun login(userName: String, password: String): UserCredentials { - return loginUseCase.login(userName, password) + override suspend fun login(request: AuthService.LoginRequest): AuthService.LoginResult { + return when { + request.serverUrl == null -> loginUseCase.login(request.userName, request.password) + else -> { + val serverUrl = HomeServerUrl(request.serverUrl.ensureHttpsIfMissing().ensureTrailingSlash()) + loginServerUseCase.login(request.userName, request.password, serverUrl) + } + } } override suspend fun register(userName: String, password: String, homeServer: String): UserCredentials { diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/FetchWellKnownUseCaseImpl.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/FetchWellKnownUseCaseImpl.kt index 308447d..0c4e8d6 100644 --- a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/FetchWellKnownUseCaseImpl.kt +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/FetchWellKnownUseCaseImpl.kt @@ -1,19 +1,50 @@ package app.dapk.st.matrix.auth.internal import app.dapk.st.matrix.http.MatrixHttpClient +import io.ktor.client.plugins.* +import io.ktor.http.* +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json +import java.net.UnknownHostException -internal typealias FetchWellKnownUseCase = suspend (String) -> ApiWellKnown +internal typealias FetchWellKnownUseCase = suspend (String) -> WellKnownResult internal class FetchWellKnownUseCaseImpl( private val httpClient: MatrixHttpClient, private val json: Json, ) : FetchWellKnownUseCase { - override suspend fun invoke(domainUrl: String): ApiWellKnown { - // workaround for matrix.org not returning a content-type - val raw = httpClient.execute(wellKnownRequest(domainUrl)) - return json.decodeFromString(ApiWellKnown.serializer(), raw) + override suspend fun invoke(domainUrl: String): WellKnownResult { + return runCatching { + val rawResponse = httpClient.execute(rawWellKnownRequestForServersWithoutContentTypes(domainUrl)) + json.decodeFromString(ApiWellKnown.serializer(), rawResponse) + } + .fold( + onSuccess = { WellKnownResult.Success(it) }, + onFailure = { + when (it) { + is UnknownHostException -> WellKnownResult.MissingWellKnown + is ClientRequestException -> when { + it.response.status.is404() -> WellKnownResult.MissingWellKnown + else -> WellKnownResult.Error(it) + } + is SerializationException -> WellKnownResult.InvalidWellKnown + else -> WellKnownResult.Error(it) + } + }, + ) } -} \ No newline at end of file + private fun rawWellKnownRequestForServersWithoutContentTypes(domainUrl: String) = wellKnownRequest(domainUrl) + +} + +sealed interface WellKnownResult { + data class Success(val wellKnown: ApiWellKnown) : WellKnownResult + object MissingWellKnown : WellKnownResult + object InvalidWellKnown : WellKnownResult + data class Error(val cause: Throwable) : WellKnownResult + +} + +fun HttpStatusCode.is404() = this.value == 404 \ No newline at end of file diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordServerUseCase.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordServerUseCase.kt new file mode 100644 index 0000000..aac15fe --- /dev/null +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordServerUseCase.kt @@ -0,0 +1,33 @@ +package app.dapk.st.matrix.auth.internal + +import app.dapk.st.matrix.auth.AuthService +import app.dapk.st.matrix.common.CredentialsStore +import app.dapk.st.matrix.common.HomeServerUrl +import app.dapk.st.matrix.common.UserCredentials +import app.dapk.st.matrix.common.UserId +import app.dapk.st.matrix.http.MatrixHttpClient + +class LoginWithUserPasswordServerUseCase( + private val httpClient: MatrixHttpClient, + private val credentialsProvider: CredentialsStore, +) { + + suspend fun login(userName: String, password: String, serverUrl: HomeServerUrl): AuthService.LoginResult { + return runCatching { + authenticate(serverUrl, UserId(userName.substringBefore(":")), password) + }.fold( + onSuccess = { AuthService.LoginResult.Success(it) }, + onFailure = { AuthService.LoginResult.Error(it) } + ) + } + + private suspend fun authenticate(baseUrl: HomeServerUrl, fullUserId: UserId, password: String): UserCredentials { + val authResponse = httpClient.execute(loginRequest(fullUserId, password, baseUrl.value)) + return UserCredentials( + authResponse.accessToken, + baseUrl, + authResponse.userId, + authResponse.deviceId, + ).also { credentialsProvider.update(it) } + } +} \ No newline at end of file diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginUseCase.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordUseCase.kt similarity index 63% rename from matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginUseCase.kt rename to matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordUseCase.kt index ac73a8a..94eba51 100644 --- a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginUseCase.kt +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/LoginWithUserPasswordUseCase.kt @@ -1,6 +1,6 @@ package app.dapk.st.matrix.auth.internal -import app.dapk.st.matrix.auth.AuthConfig +import app.dapk.st.matrix.auth.AuthService import app.dapk.st.matrix.common.CredentialsStore import app.dapk.st.matrix.common.HomeServerUrl import app.dapk.st.matrix.common.UserCredentials @@ -10,23 +10,27 @@ import app.dapk.st.matrix.http.ensureTrailingSlash private const val MATRIX_DOT_ORG_DOMAIN = "matrix.org" -class LoginUseCase( +class LoginWithUserPasswordUseCase( private val httpClient: MatrixHttpClient, private val credentialsProvider: CredentialsStore, private val fetchWellKnownUseCase: FetchWellKnownUseCase, - private val authConfig: AuthConfig ) { - suspend fun login(userName: String, password: String): UserCredentials { + suspend fun login(userName: String, password: String): AuthService.LoginResult { val (domainUrl, fullUserId) = generateUserAccessInfo(userName) - val baseUrl = fetchWellKnownUseCase(domainUrl).homeServer.baseUrl.ensureTrailingSlash() - val authResponse = httpClient.execute(loginRequest(fullUserId, password, baseUrl.value)) - return UserCredentials( - authResponse.accessToken, - baseUrl, - authResponse.userId, - authResponse.deviceId, - ).also { credentialsProvider.update(it) } + return when (val wellKnownResult = fetchWellKnownUseCase(domainUrl)) { + is WellKnownResult.Success -> { + runCatching { + authenticate(wellKnownResult.wellKnown.homeServer.baseUrl.ensureTrailingSlash(), fullUserId, password) + }.fold( + onSuccess = { AuthService.LoginResult.Success(it) }, + onFailure = { AuthService.LoginResult.Error(it) } + ) + } + WellKnownResult.InvalidWellKnown -> AuthService.LoginResult.MissingWellKnown + WellKnownResult.MissingWellKnown -> AuthService.LoginResult.MissingWellKnown + is WellKnownResult.Error -> AuthService.LoginResult.Error(wellKnownResult.cause) + } } private fun generateUserAccessInfo(userName: String): Pair { @@ -37,14 +41,20 @@ class LoginUseCase( return Pair(domainUrl, UserId(fullUserId)) } + private suspend fun authenticate(baseUrl: HomeServerUrl, fullUserId: UserId, password: String): UserCredentials { + val authResponse = httpClient.execute(loginRequest(fullUserId, password, baseUrl.value)) + return UserCredentials( + authResponse.accessToken, + baseUrl, + authResponse.userId, + authResponse.deviceId, + ).also { credentialsProvider.update(it) } + } + private fun String.findDomain(fallback: String) = this.substringAfter(":", missingDelimiterValue = fallback) private fun String.asHttpsUrl(): String { - val schema = when (authConfig.forceHttp) { - true -> "http://" - false -> "https://" - } - return "$schema$this".ensureTrailingSlash() + return "https://$this".ensureTrailingSlash() } } diff --git a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/RegisterUseCase.kt b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/RegisterUseCase.kt index 320ab3b..d611e88 100644 --- a/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/RegisterUseCase.kt +++ b/matrix/services/auth/src/main/kotlin/app/dapk/st/matrix/auth/internal/RegisterUseCase.kt @@ -1,6 +1,5 @@ package app.dapk.st.matrix.auth.internal -import app.dapk.st.matrix.auth.AuthConfig import app.dapk.st.matrix.common.CredentialsStore import app.dapk.st.matrix.common.UserCredentials import app.dapk.st.matrix.http.MatrixHttpClient @@ -16,7 +15,6 @@ class RegisterUseCase( private val credentialsProvider: CredentialsStore, private val json: Json, private val fetchWellKnownUseCase: FetchWellKnownUseCase, - private val authConfig: AuthConfig, ) { suspend fun register(userName: String, password: String, homeServer: String): UserCredentials { @@ -46,7 +44,12 @@ class RegisterUseCase( registerRequest(userName, password, baseUrl, Auth(session, "m.login.dummy")) ) val homeServerUrl = when (authResponse.wellKnown == null) { - true -> fetchWellKnownUseCase(baseUrl).homeServer.baseUrl + true -> when (val wellKnownResult = fetchWellKnownUseCase(baseUrl)) { + is WellKnownResult.Error, -> TODO() + WellKnownResult.InvalidWellKnown -> TODO() + WellKnownResult.MissingWellKnown -> TODO() + is WellKnownResult.Success -> wellKnownResult.wellKnown.homeServer.baseUrl + } false -> authResponse.wellKnown.homeServer.baseUrl } return UserCredentials( diff --git a/test-harness/src/test/kotlin/SmokeTest.kt b/test-harness/src/test/kotlin/SmokeTest.kt index 438452a..b4831e3 100644 --- a/test-harness/src/test/kotlin/SmokeTest.kt +++ b/test-harness/src/test/kotlin/SmokeTest.kt @@ -1,3 +1,4 @@ +import app.dapk.st.matrix.auth.AuthService import app.dapk.st.matrix.auth.authService import app.dapk.st.matrix.common.HomeServerUrl import app.dapk.st.matrix.common.RoomId @@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeInstanceOf import org.amshove.kluent.shouldNotBeEqualTo import org.junit.jupiter.api.MethodOrderer import org.junit.jupiter.api.Order @@ -131,13 +133,17 @@ private suspend fun login(user: TestUser) { val result = testMatrix .client .authService() - .login(userName = user.roomMember.id.value, password = user.password) + .login(AuthService.LoginRequest(userName = user.roomMember.id.value, password = user.password, serverUrl = null)) - result.accessToken shouldNotBeEqualTo null - result.homeServer shouldBeEqualTo HomeServerUrl(HTTPS_TEST_SERVER_URL) - result.userId shouldBeEqualTo user.roomMember.id - testMatrix.saveLogin(result) + result shouldBeInstanceOf AuthService.LoginResult.Success::class.java + (result as AuthService.LoginResult.Success).userCredentials.let { credentials -> + credentials.accessToken shouldNotBeEqualTo null + credentials.homeServer shouldBeEqualTo HomeServerUrl(HTTPS_TEST_SERVER_URL) + credentials.userId shouldBeEqualTo user.roomMember.id + + testMatrix.saveLogin(credentials) + } } object SharedState { diff --git a/test-harness/src/test/kotlin/test/TestMatrix.kt b/test-harness/src/test/kotlin/test/TestMatrix.kt index 39ce8ed..10c966e 100644 --- a/test-harness/src/test/kotlin/test/TestMatrix.kt +++ b/test-harness/src/test/kotlin/test/TestMatrix.kt @@ -6,7 +6,7 @@ import app.dapk.st.core.CoroutineDispatchers import app.dapk.st.core.SingletonFlows import app.dapk.st.domain.StoreModule import app.dapk.st.matrix.MatrixClient -import app.dapk.st.matrix.auth.AuthConfig +import app.dapk.st.matrix.auth.AuthService import app.dapk.st.matrix.auth.authService import app.dapk.st.matrix.auth.installAuthService import app.dapk.st.matrix.common.* @@ -79,7 +79,7 @@ class TestMatrix( logger ).also { it.install { - installAuthService(storeModule.credentialsStore(), AuthConfig(forceHttp = false)) + installAuthService(storeModule.credentialsStore()) installEncryptionService(storeModule.knownDevicesStore()) val base64 = JavaBase64() @@ -255,7 +255,7 @@ class TestMatrix( suspend fun newlogin() { client.authService() - .login(user.roomMember.id.value, user.password) + .login(AuthService.LoginRequest(user.roomMember.id.value, user.password, null)) } suspend fun restoreLogin() {