Starting process of retrieving login credentials from credential manager
This commit is contained in:
parent
2a69fbcef7
commit
26c26ed054
8 changed files with 100 additions and 22 deletions
|
@ -1,7 +1,7 @@
|
||||||
CREATE TABLE IF NOT EXISTS
|
CREATE TABLE IF NOT EXISTS
|
||||||
public.status (
|
public.status (
|
||||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
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(),
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
updated_at TIMESTAMP WITH TIME ZONE NULL
|
updated_at TIMESTAMP WITH TIME ZONE NULL
|
||||||
);
|
);
|
||||||
|
@ -21,9 +21,9 @@ VALUES
|
||||||
CREATE TABLE IF NOT EXISTS
|
CREATE TABLE IF NOT EXISTS
|
||||||
public.user (
|
public.user (
|
||||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
email VARCHAR(255) NOT NULL,
|
email TEXT NOT NULL,
|
||||||
password VARCHAR(97) NOT NULL,
|
password TEXT NOT NULL,
|
||||||
name VARCHAR(255) NOT NULL,
|
name TEXT NOT NULL,
|
||||||
status_id INT NOT NULL REFERENCES status(id) DEFAULT 2,
|
status_id INT NOT NULL REFERENCES status(id) DEFAULT 2,
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
updated_at TIMESTAMP WITH TIME ZONE NULL
|
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
|
CREATE TABLE IF NOT EXISTS
|
||||||
public.permission (
|
public.permission (
|
||||||
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
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,
|
status_id INT NOT NULL REFERENCES status(id) DEFAULT 1,
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||||
updated_at TIMESTAMP WITH TIME ZONE NULL
|
updated_at TIMESTAMP WITH TIME ZONE NULL
|
||||||
|
|
|
@ -27,9 +27,11 @@ import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
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.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import ing.bikeshedengineer.debtpirate.domain.model.AccountManagerResult
|
||||||
import ing.bikeshedengineer.debtpirate.domain.repository.AccountManager
|
import ing.bikeshedengineer.debtpirate.domain.repository.AccountManager
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -53,6 +57,20 @@ fun LoginScreen(
|
||||||
AccountManager(context)
|
AccountManager(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
LaunchedEffect(true) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val result = accountManager.getCredentials()
|
||||||
|
when (result) {
|
||||||
|
is AccountManagerResult.FoundCredentials -> {
|
||||||
|
val (emailAddress, password) = result
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
|
@ -74,7 +92,7 @@ fun LoginScreen(
|
||||||
LoginComponent(
|
LoginComponent(
|
||||||
emailAddress = emailAddress,
|
emailAddress = emailAddress,
|
||||||
onUpdateEmailAddress = {
|
onUpdateEmailAddress = {
|
||||||
viewModel.updateState(
|
viewModel.onAction(
|
||||||
LoginScreenStateAction.UpdateEmailAddress(
|
LoginScreenStateAction.UpdateEmailAddress(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
@ -82,13 +100,20 @@ fun LoginScreen(
|
||||||
},
|
},
|
||||||
password = password,
|
password = password,
|
||||||
onUpdatePassword = {
|
onUpdatePassword = {
|
||||||
viewModel.updateState(
|
viewModel.onAction(
|
||||||
LoginScreenStateAction.UpdatePassword(
|
LoginScreenStateAction.UpdatePassword(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
submitLoginRequest = viewModel::submitLoginRequest
|
submitLoginRequest = {
|
||||||
|
viewModel.onAction(
|
||||||
|
LoginScreenStateAction.ValidateCredentials(
|
||||||
|
emailAddress.value,
|
||||||
|
password.value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Separator(
|
Separator(
|
||||||
|
|
|
@ -3,4 +3,6 @@ package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login
|
||||||
sealed interface LoginScreenStateAction {
|
sealed interface LoginScreenStateAction {
|
||||||
data class UpdateEmailAddress(val emailAddress: String) : LoginScreenStateAction
|
data class UpdateEmailAddress(val emailAddress: String) : LoginScreenStateAction
|
||||||
data class UpdatePassword(val password: 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
|
||||||
}
|
}
|
|
@ -41,15 +41,13 @@ class LoginScreenViewModel @Inject constructor(
|
||||||
private val submitLoginCredentials: SubmitLoginCredentialsUseCase,
|
private val submitLoginCredentials: SubmitLoginCredentialsUseCase,
|
||||||
private val validateLoginCredentials: ValidateLoginCredentialsUseCase,
|
private val validateLoginCredentials: ValidateLoginCredentialsUseCase,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
// private val storeLoginData = StoreLoginDataUseCase(dataStore)
|
|
||||||
|
|
||||||
private val _emailAddress = MutableStateFlow("")
|
private val _emailAddress = MutableStateFlow("")
|
||||||
val emailAddress = _emailAddress.asStateFlow()
|
val emailAddress = _emailAddress.asStateFlow()
|
||||||
|
|
||||||
private val _password = MutableStateFlow("")
|
private val _password = MutableStateFlow("")
|
||||||
val password = _password.asStateFlow()
|
val password = _password.asStateFlow()
|
||||||
|
|
||||||
fun updateState(action: LoginScreenStateAction) {
|
fun onAction(action: LoginScreenStateAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is LoginScreenStateAction.UpdateEmailAddress -> {
|
is LoginScreenStateAction.UpdateEmailAddress -> {
|
||||||
_emailAddress.value = action.emailAddress
|
_emailAddress.value = action.emailAddress
|
||||||
|
@ -58,19 +56,25 @@ class LoginScreenViewModel @Inject constructor(
|
||||||
is LoginScreenStateAction.UpdatePassword -> {
|
is LoginScreenStateAction.UpdatePassword -> {
|
||||||
_password.value = action.password
|
_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() {
|
private fun onValidateLoginCredentials(emailAddress: String, password: String) {
|
||||||
when (validateLoginCredentials(emailAddress.value, password.value)) {
|
when (validateLoginCredentials(emailAddress, password)) {
|
||||||
is LoginCredentialsValidationResult.ValidCredentials -> {
|
is LoginCredentialsValidationResult.ValidCredentials -> {
|
||||||
viewModelScope.launch {
|
onAction(LoginScreenStateAction.SubmitLoginRequest(emailAddress, password))
|
||||||
try {
|
|
||||||
val result = submitLoginCredentials(emailAddress.value, password.value)
|
|
||||||
} catch (err: Throwable) {
|
|
||||||
// TODO...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
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) {
|
fun storeAuthData(userId: Int, sessionToken: Token, authToken: Token) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
prefsStore.updateData { currentPrefs ->
|
prefsStore.updateData { currentPrefs ->
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register
|
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
|
@ -6,5 +6,10 @@ sealed interface RegistrationScreenAction {
|
||||||
data class UpdatePassword(val password: String) : RegistrationScreenAction
|
data class UpdatePassword(val password: String) : RegistrationScreenAction
|
||||||
data class UpdateConfirmPassword(val confirmPassword: String) : RegistrationScreenAction
|
data class UpdateConfirmPassword(val confirmPassword: String) : RegistrationScreenAction
|
||||||
data object ResetFields : 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
|
||||||
}
|
}
|
|
@ -2,7 +2,9 @@ package ing.bikeshedengineer.debtpirate.domain.model
|
||||||
|
|
||||||
sealed interface AccountManagerResult {
|
sealed interface AccountManagerResult {
|
||||||
data object Success : AccountManagerResult
|
data object Success : AccountManagerResult
|
||||||
|
data class FoundCredentials(val emailAddress: String, val password: String) : AccountManagerResult
|
||||||
data object Unavailable : AccountManagerResult
|
data object Unavailable : AccountManagerResult
|
||||||
data object Canceled : AccountManagerResult
|
data object Canceled : AccountManagerResult
|
||||||
data object Failure : AccountManagerResult
|
data object Failure : AccountManagerResult
|
||||||
|
data object Invalid : AccountManagerResult
|
||||||
}
|
}
|
|
@ -4,6 +4,9 @@ import android.app.Activity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.credentials.CreatePasswordRequest
|
import androidx.credentials.CreatePasswordRequest
|
||||||
import androidx.credentials.CredentialManager
|
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.CreateCredentialCancellationException
|
||||||
import androidx.credentials.exceptions.CreateCredentialException
|
import androidx.credentials.exceptions.CreateCredentialException
|
||||||
import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
|
import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
|
||||||
|
@ -30,11 +33,41 @@ class AccountManager(private val context: Activity) {
|
||||||
"DebtPirate::AccountManager",
|
"DebtPirate::AccountManager",
|
||||||
"Cannot store credentials; a Google account isn't associated with this device"
|
"Cannot store credentials; a Google account isn't associated with this device"
|
||||||
)
|
)
|
||||||
|
|
||||||
AccountManagerResult.Unavailable
|
AccountManagerResult.Unavailable
|
||||||
} catch (_: CreateCredentialCancellationException) {
|
} catch (_: CreateCredentialCancellationException) {
|
||||||
AccountManagerResult.Canceled
|
AccountManagerResult.Canceled
|
||||||
} catch (err: CreateCredentialException) {
|
} catch (err: CreateCredentialException) {
|
||||||
err.printStackTrace()
|
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
|
AccountManagerResult.Failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue