Implement the simplest account manager authentication
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.auth
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthStorage {
|
||||
|
||||
val authHeaderFlow: Flow<String?>
|
||||
|
||||
suspend fun storeAuthData(authHeader: String)
|
||||
|
||||
suspend fun getAuthHeader(): String?
|
||||
|
||||
suspend fun clearAuthData()
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package gq.kirmanak.mealient.data.auth.impl
|
||||
|
||||
import gq.kirmanak.mealient.data.auth.AuthDataSource
|
||||
import android.accounts.Account
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.service.auth.AccountManagerInteractor
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
@@ -11,28 +12,59 @@ import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AuthRepoImpl @Inject constructor(
|
||||
private val dataSource: AuthDataSource,
|
||||
private val storage: AuthStorage,
|
||||
private val accountManagerInteractor: AccountManagerInteractor,
|
||||
) : AuthRepo {
|
||||
|
||||
override val isAuthorizedFlow: Flow<Boolean>
|
||||
get() = storage.authHeaderFlow.map { it != null }
|
||||
get() = accountManagerInteractor.accountUpdatesFlow()
|
||||
.map { it.firstOrNull() }
|
||||
.map { account ->
|
||||
runCatchingExceptCancel { getAuthToken(account) }
|
||||
.onFailure { Timber.e(it, "authHeaderObservable: can't get token") }
|
||||
.getOrNull()
|
||||
}.map { it != null }
|
||||
|
||||
override suspend fun authenticate(username: String, password: String) {
|
||||
Timber.v("authenticate() called with: username = $username, password = $password")
|
||||
val accessToken = dataSource.authenticate(username, password)
|
||||
Timber.d("authenticate result is \"$accessToken\"")
|
||||
storage.storeAuthData(AUTH_HEADER_FORMAT.format(accessToken))
|
||||
val account = accountManagerInteractor.addAccount(username, password)
|
||||
runCatchingExceptCancel {
|
||||
getAuthToken(account) // Try to get token to check if password is correct
|
||||
}.onFailure {
|
||||
Timber.e(it, "authenticate: can't authorize")
|
||||
removeAccount(account) // Remove account with incorrect password
|
||||
}.onSuccess {
|
||||
Timber.d("authenticate: successfully authorized")
|
||||
}.getOrThrow() // Throw error to show it to user
|
||||
}
|
||||
|
||||
override suspend fun getAuthHeader(): String? = storage.getAuthHeader()
|
||||
override suspend fun getAuthHeader(): String? {
|
||||
Timber.v("getAuthHeader() called")
|
||||
return currentAccount()
|
||||
?.let { getAuthToken(it) }
|
||||
?.let { AUTH_HEADER_FORMAT.format(it) }
|
||||
}
|
||||
|
||||
private suspend fun getAuthToken(account: Account?): String? {
|
||||
return account?.let { accountManagerInteractor.getAuthToken(it) }
|
||||
}
|
||||
|
||||
private fun currentAccount(): Account? {
|
||||
val account = accountManagerInteractor.getAccounts().firstOrNull()
|
||||
Timber.v("currentAccount() returned: $account")
|
||||
return account
|
||||
}
|
||||
|
||||
override suspend fun requireAuthHeader(): String =
|
||||
checkNotNull(getAuthHeader()) { "Auth header is null when it was required" }
|
||||
|
||||
override suspend fun logout() {
|
||||
Timber.v("logout() called")
|
||||
storage.clearAuthData()
|
||||
currentAccount()?.let { removeAccount(it) }
|
||||
}
|
||||
|
||||
private suspend fun removeAccount(account: Account) {
|
||||
Timber.v("removeAccount() called with: account = $account")
|
||||
accountManagerInteractor.removeAccount(account)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package gq.kirmanak.mealient.data.auth.impl
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import gq.kirmanak.mealient.data.auth.AuthStorage
|
||||
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||
import gq.kirmanak.mealient.service.auth.AccountManagerInteractor
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ActivityScoped
|
||||
class AuthStorageImpl @Inject constructor(
|
||||
private val accountManagerInteractorImpl: AccountManagerInteractor,
|
||||
private val preferencesStorage: PreferencesStorage,
|
||||
) : AuthStorage {
|
||||
|
||||
private val authHeaderKey: Preferences.Key<String>
|
||||
get() = preferencesStorage.authHeaderKey
|
||||
override val authHeaderFlow: Flow<String?>
|
||||
get() = preferencesStorage.valueUpdates(authHeaderKey)
|
||||
|
||||
override suspend fun storeAuthData(authHeader: String) {
|
||||
Timber.v("storeAuthData() called with: authHeader = $authHeader")
|
||||
preferencesStorage.storeValues(Pair(authHeaderKey, authHeader))
|
||||
}
|
||||
|
||||
override suspend fun getAuthHeader(): String? {
|
||||
Timber.v("getAuthHeader() called")
|
||||
val token = preferencesStorage.getValue(authHeaderKey)
|
||||
Timber.d("getAuthHeader: header is \"$token\"")
|
||||
return token
|
||||
}
|
||||
|
||||
override fun authHeaderObservable(): Flow<String?> {
|
||||
Timber.v("authHeaderObservable() called")
|
||||
return accountManagerInteractorImpl.accountUpdatesFlow()
|
||||
.map { it.firstOrNull() }
|
||||
.map { account ->
|
||||
account ?: return@map null
|
||||
runCatching { accountManagerInteractorImpl.getAuthToken(account) }
|
||||
.onFailure { Timber.e(it, "authHeaderObservable: can't get token") }
|
||||
.getOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clearAuthData() {
|
||||
Timber.v("clearAuthData() called")
|
||||
preferencesStorage.removeValues(authHeaderKey)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user