Starting process of retrieving login credentials from credential manager

This commit is contained in:
Z. Charles Dziura 2024-11-15 20:46:10 -05:00
parent 2a69fbcef7
commit 26c26ed054
8 changed files with 100 additions and 22 deletions

View file

@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS
public.status (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(255) NOT NULL,
name TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
);
@ -21,9 +21,9 @@ VALUES
CREATE TABLE IF NOT EXISTS
public.user (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email VARCHAR(255) NOT NULL,
password VARCHAR(97) NOT NULL,
name VARCHAR(255) NOT NULL,
email TEXT NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL,
status_id INT NOT NULL REFERENCES status(id) DEFAULT 2,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL
@ -34,7 +34,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS user_email_uniq_idx ON public.user(email);
CREATE TABLE IF NOT EXISTS
public.permission (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(255) NOT NULL,
name TEXT NOT NULL,
status_id INT NOT NULL REFERENCES status(id) DEFAULT 1,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NULL

View file

@ -27,9 +27,11 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -41,7 +43,9 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import ing.bikeshedengineer.debtpirate.domain.model.AccountManagerResult
import ing.bikeshedengineer.debtpirate.domain.repository.AccountManager
import kotlinx.coroutines.launch
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
@ -53,6 +57,20 @@ fun LoginScreen(
AccountManager(context)
}
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(true) {
coroutineScope.launch {
val result = accountManager.getCredentials()
when (result) {
is AccountManagerResult.FoundCredentials -> {
val (emailAddress, password) = result
}
else -> {}
}
}
}
Scaffold(
modifier = Modifier.fillMaxSize()
) {
@ -74,7 +92,7 @@ fun LoginScreen(
LoginComponent(
emailAddress = emailAddress,
onUpdateEmailAddress = {
viewModel.updateState(
viewModel.onAction(
LoginScreenStateAction.UpdateEmailAddress(
it
)
@ -82,13 +100,20 @@ fun LoginScreen(
},
password = password,
onUpdatePassword = {
viewModel.updateState(
viewModel.onAction(
LoginScreenStateAction.UpdatePassword(
it
)
)
},
submitLoginRequest = viewModel::submitLoginRequest
submitLoginRequest = {
viewModel.onAction(
LoginScreenStateAction.ValidateCredentials(
emailAddress.value,
password.value
)
)
}
)
Separator(

View file

@ -3,4 +3,6 @@ package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login
sealed interface LoginScreenStateAction {
data class UpdateEmailAddress(val emailAddress: String) : LoginScreenStateAction
data class UpdatePassword(val password: String) : LoginScreenStateAction
data class ValidateCredentials(val emailAddress: String, val password: String) : LoginScreenStateAction
data class SubmitLoginRequest(val emailAddress: String, val password: String) : LoginScreenStateAction
}

View file

@ -41,15 +41,13 @@ class LoginScreenViewModel @Inject constructor(
private val submitLoginCredentials: SubmitLoginCredentialsUseCase,
private val validateLoginCredentials: ValidateLoginCredentialsUseCase,
) : ViewModel() {
// private val storeLoginData = StoreLoginDataUseCase(dataStore)
private val _emailAddress = MutableStateFlow("")
val emailAddress = _emailAddress.asStateFlow()
private val _password = MutableStateFlow("")
val password = _password.asStateFlow()
fun updateState(action: LoginScreenStateAction) {
fun onAction(action: LoginScreenStateAction) {
when (action) {
is LoginScreenStateAction.UpdateEmailAddress -> {
_emailAddress.value = action.emailAddress
@ -58,19 +56,25 @@ class LoginScreenViewModel @Inject constructor(
is LoginScreenStateAction.UpdatePassword -> {
_password.value = action.password
}
is LoginScreenStateAction.ValidateCredentials -> {
val (emailAddress, password) = action
onValidateLoginCredentials(emailAddress, password)
}
is LoginScreenStateAction.SubmitLoginRequest -> {
viewModelScope.launch {
val (emailAddress, password) = action
onSubmitLoginRequest(emailAddress, password)
}
}
}
}
fun submitLoginRequest() {
when (validateLoginCredentials(emailAddress.value, password.value)) {
private fun onValidateLoginCredentials(emailAddress: String, password: String) {
when (validateLoginCredentials(emailAddress, password)) {
is LoginCredentialsValidationResult.ValidCredentials -> {
viewModelScope.launch {
try {
val result = submitLoginCredentials(emailAddress.value, password.value)
} catch (err: Throwable) {
// TODO...
}
}
onAction(LoginScreenStateAction.SubmitLoginRequest(emailAddress, password))
}
else -> {
@ -80,6 +84,14 @@ class LoginScreenViewModel @Inject constructor(
}
private suspend fun onSubmitLoginRequest(emailAddress: String, password: String) {
try {
val result = submitLoginCredentials(emailAddress, password)
} catch (err: Throwable) {
// TODO...
}
}
fun storeAuthData(userId: Int, sessionToken: Token, authToken: Token) {
viewModelScope.launch {
prefsStore.updateData { currentPrefs ->

View file

@ -1,7 +1,6 @@
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register
import android.annotation.SuppressLint
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Column

View file

@ -6,5 +6,10 @@ sealed interface RegistrationScreenAction {
data class UpdatePassword(val password: String) : RegistrationScreenAction
data class UpdateConfirmPassword(val confirmPassword: String) : RegistrationScreenAction
data object ResetFields : RegistrationScreenAction
data class RegisterNewUser(val emailAddress: String, val name: String, val password: String, val confirmPassword: String) : RegistrationScreenAction
data class RegisterNewUser(
val emailAddress: String,
val name: String,
val password: String,
val confirmPassword: String
) : RegistrationScreenAction
}

View file

@ -2,7 +2,9 @@ package ing.bikeshedengineer.debtpirate.domain.model
sealed interface AccountManagerResult {
data object Success : AccountManagerResult
data class FoundCredentials(val emailAddress: String, val password: String) : AccountManagerResult
data object Unavailable : AccountManagerResult
data object Canceled : AccountManagerResult
data object Failure : AccountManagerResult
data object Invalid : AccountManagerResult
}

View file

@ -4,6 +4,9 @@ import android.app.Activity
import android.util.Log
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.CredentialManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetPasswordOption
import androidx.credentials.PasswordCredential
import androidx.credentials.exceptions.CreateCredentialCancellationException
import androidx.credentials.exceptions.CreateCredentialException
import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
@ -30,11 +33,41 @@ class AccountManager(private val context: Activity) {
"DebtPirate::AccountManager",
"Cannot store credentials; a Google account isn't associated with this device"
)
AccountManagerResult.Unavailable
} catch (_: CreateCredentialCancellationException) {
AccountManagerResult.Canceled
} catch (err: CreateCredentialException) {
err.printStackTrace()
Log.i(
"DebtPirate::AccountManager",
"Unable to store credentials: ${err.message}"
)
AccountManagerResult.Failure
}
}
suspend fun getCredentials(): AccountManagerResult {
return try {
val result = credentialManager.getCredential(
context, request = GetCredentialRequest(
credentialOptions = listOf(GetPasswordOption(isAutoSelectAllowed = true))
)
)
val credentials = result.credential
when (credentials) {
is PasswordCredential -> {
val emailAddress = credentials.id
val password = credentials.password
AccountManagerResult.FoundCredentials(emailAddress, password)
}
else -> AccountManagerResult.Invalid
}
} catch (err: Exception) {
AccountManagerResult.Failure
}
}