From dff518bde7f03e3f2ac9bd7a4e1f631f5d4c5cea Mon Sep 17 00:00:00 2001 From: Infi Date: Tue, 6 Dec 2022 00:49:35 +0100 Subject: [PATCH] feat: improvements at login - password field is now recognised by the system keyboard - TOTP six-digit input is now a numbers-only field - document API functions related to login - basic logout in API - insecure MFA-less accounts can now log in (SAD!) --- .../main/java/chat/revolt/api/RevoltAPI.kt | 18 ++++++++++++- .../components/generic/FormTextField.kt | 7 +++-- .../chat/revolt/screens/login/LoginScreen.kt | 27 ++++++++++++------- .../chat/revolt/screens/login/MfaScreen.kt | 2 ++ 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/chat/revolt/api/RevoltAPI.kt b/app/src/main/java/chat/revolt/api/RevoltAPI.kt index 119eeb71..4ef2dcfe 100644 --- a/app/src/main/java/chat/revolt/api/RevoltAPI.kt +++ b/app/src/main/java/chat/revolt/api/RevoltAPI.kt @@ -54,7 +54,9 @@ val RevoltHttp = HttpClient(OkHttp) { object RevoltAPI { const val TOKEN_HEADER_NAME = "x-session-token" - val userCache = mutableMapOf() + // discount caching solution(/-s)! LRU would be better but this is fine for now, until it's not... + val userCache = + mutableMapOf() var selfId: String? = null @@ -71,9 +73,23 @@ object RevoltAPI { } } + /** + * Returns true if the user is logged in and the current user has been fetched at least once. + * Call [initialize] to fetch the current user first, else this will return false. + */ fun isLoggedIn(): Boolean { return selfId != null } + + /** + * Clears the API client's state completely. + */ + fun logout() { + selfId = null + sessionToken = "" + + userCache.clear() + } } @kotlinx.serialization.Serializable diff --git a/app/src/main/java/chat/revolt/components/generic/FormTextField.kt b/app/src/main/java/chat/revolt/components/generic/FormTextField.kt index ce42dbb6..1aa610ad 100644 --- a/app/src/main/java/chat/revolt/components/generic/FormTextField.kt +++ b/app/src/main/java/chat/revolt/components/generic/FormTextField.kt @@ -1,10 +1,12 @@ package chat.revolt.components.generic +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation @@ -15,13 +17,14 @@ fun FormTextField( label: String, onChange: (it: String) -> Unit, modifier: Modifier = Modifier, - password: Boolean = false, + type: KeyboardType = KeyboardType.Text, ) { TextField( value = value, onValueChange = onChange, singleLine = true, - visualTransformation = if (password) PasswordVisualTransformation() else VisualTransformation.None, + keyboardOptions = KeyboardOptions(keyboardType = type), + visualTransformation = if (type == KeyboardType.Password) PasswordVisualTransformation() else VisualTransformation.None, label = { Text(label) }, modifier = modifier ) diff --git a/app/src/main/java/chat/revolt/screens/login/LoginScreen.kt b/app/src/main/java/chat/revolt/screens/login/LoginScreen.kt index e97990f7..c2de63b2 100644 --- a/app/src/main/java/chat/revolt/screens/login/LoginScreen.kt +++ b/app/src/main/java/chat/revolt/screens/login/LoginScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -22,6 +23,7 @@ import chat.revolt.R import chat.revolt.api.REVOLT_SUPPORT import chat.revolt.api.routes.account.EmailPasswordAssessment import chat.revolt.api.routes.account.negotiateAuthentication +import chat.revolt.api.routes.user.fetchSelfWithNewToken import chat.revolt.components.generic.AnyLink import chat.revolt.components.generic.FormTextField import chat.revolt.components.generic.Weblink @@ -40,9 +42,9 @@ class LoginViewModel() : ViewModel() { val error: String? get() = _error - private var _navigateToMfa by mutableStateOf(false) - val navigateToMfa: Boolean - get() = _navigateToMfa + private var _navigateTo by mutableStateOf(null) + val navigateTo: String? + get() = _navigateTo private var _mfaResponse by mutableStateOf(null) val mfaResponse: EmailPasswordAssessment? @@ -60,19 +62,21 @@ class LoginViewModel() : ViewModel() { if (response.proceedMfa) { Log.d("Login", "MFA required. Navigating to MFA screen") _mfaResponse = response - _navigateToMfa = true + _navigateTo = "mfa" } else { Log.d( "Login", "No MFA required. Login is complete! We have a session token: ${response.firstUserHints!!.token}" ) + fetchSelfWithNewToken(response.firstUserHints.token) + _navigateTo = "home" } } } } - fun mfaComplete() { - _navigateToMfa = false + fun navigationComplete() { + _navigateTo = null } fun setEmail(email: String) { @@ -89,7 +93,7 @@ fun LoginScreen( navController: NavController, viewModel: LoginViewModel = viewModel() ) { - if (viewModel.navigateToMfa) { + if (viewModel.navigateTo == "mfa") { navController.navigate( "setup/mfa/${viewModel.mfaResponse!!.mfaSpec!!.ticket}/${ viewModel.mfaResponse!!.mfaSpec!!.allowedMethods.joinToString( @@ -97,7 +101,12 @@ fun LoginScreen( ) }" ) - viewModel.mfaComplete() + viewModel.navigationComplete() + } else if (viewModel.navigateTo == "home") { + navController.navigate("chat/home") { + popUpTo("setup/greeting") { inclusive = true } + } + viewModel.navigationComplete() } Column( @@ -143,7 +152,7 @@ fun LoginScreen( FormTextField( value = viewModel.password, label = stringResource(R.string.password), - password = true, + type = KeyboardType.Password, onChange = { viewModel.setPassword(it) }) AnyLink( diff --git a/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt b/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt index 910846fa..68bfcf4c 100644 --- a/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt +++ b/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -197,6 +198,7 @@ fun MfaScreen( FormTextField( label = stringResource(R.string.mfa_totp_code), onChange = { viewModel.setTotpCode(it) }, + type = KeyboardType.Number, value = viewModel.totpCode, )