Create API token when signing in

This commit is contained in:
Kirill Kamakin
2022-12-11 11:57:13 +01:00
parent a560db8bb6
commit c99f9fea88
21 changed files with 130 additions and 124 deletions

View File

@@ -5,4 +5,6 @@ interface AuthDataSource {
* Tries to acquire authentication token using the provided credentials
*/
suspend fun authenticate(username: String, password: String): String
suspend fun createApiToken(name: String): String
}

View File

@@ -10,9 +10,5 @@ interface AuthRepo {
suspend fun getAuthHeader(): String?
suspend fun requireAuthHeader(): String
suspend fun logout()
suspend fun invalidateAuthHeader()
}

View File

@@ -9,12 +9,4 @@ interface AuthStorage {
suspend fun setAuthHeader(authHeader: String?)
suspend fun getAuthHeader(): String?
suspend fun setEmail(email: String?)
suspend fun getEmail(): String?
suspend fun setPassword(password: String?)
suspend fun getPassword(): String?
}

View File

@@ -4,7 +4,9 @@ import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.baseurl.ServerInfoRepo
import gq.kirmanak.mealient.data.baseurl.ServerVersion
import gq.kirmanak.mealient.datasource.v0.MealieDataSourceV0
import gq.kirmanak.mealient.datasource.v0.models.CreateApiTokenRequestV0
import gq.kirmanak.mealient.datasource.v1.MealieDataSourceV1
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
import javax.inject.Inject
import javax.inject.Singleton
@@ -15,14 +17,20 @@ class AuthDataSourceImpl @Inject constructor(
private val v1Source: MealieDataSourceV1,
) : AuthDataSource {
private suspend fun getVersion(): ServerVersion = serverInfoRepo.getVersion()
private suspend fun getUrl(): String = serverInfoRepo.requireUrl()
override suspend fun authenticate(
username: String,
password: String,
): String {
val baseUrl = serverInfoRepo.requireUrl()
return when (serverInfoRepo.getVersion()) {
ServerVersion.V0 -> v0Source.authenticate(baseUrl, username, password)
ServerVersion.V1 -> v1Source.authenticate(baseUrl, username, password)
}
): String = when (getVersion()) {
ServerVersion.V0 -> v0Source.authenticate(getUrl(), username, password)
ServerVersion.V1 -> v1Source.authenticate(getUrl(), username, password)
}
override suspend fun createApiToken(name: String): String = when (getVersion()) {
ServerVersion.V0 -> v0Source.createApiToken(getUrl(), CreateApiTokenRequestV0(name))
ServerVersion.V1 -> v1Source.createApiToken(getUrl(), CreateApiTokenRequestV1(name)).token
}
}

View File

@@ -4,7 +4,6 @@ import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.auth.AuthRepo
import gq.kirmanak.mealient.data.auth.AuthStorage
import gq.kirmanak.mealient.datasource.AuthenticationProvider
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
import gq.kirmanak.mealient.logging.Logger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -24,34 +23,20 @@ class AuthRepoImpl @Inject constructor(
override suspend fun authenticate(email: String, password: String) {
logger.v { "authenticate() called with: email = $email, password = $password" }
val token = authDataSource.authenticate(email, password)
val header = AUTH_HEADER_FORMAT.format(token)
authStorage.setAuthHeader(header)
authStorage.setEmail(email)
authStorage.setPassword(password)
authStorage.setAuthHeader(AUTH_HEADER_FORMAT.format(token))
val apiToken = authDataSource.createApiToken(API_TOKEN_NAME)
authStorage.setAuthHeader(AUTH_HEADER_FORMAT.format(apiToken))
}
override suspend fun getAuthHeader(): String? = authStorage.getAuthHeader()
override suspend fun requireAuthHeader(): String = checkNotNull(getAuthHeader()) {
"Auth header is null when it was required"
}
override suspend fun logout() {
logger.v { "logout() called" }
authStorage.setEmail(null)
authStorage.setPassword(null)
authStorage.setAuthHeader(null)
}
override suspend fun invalidateAuthHeader() {
logger.v { "invalidateAuthHeader() called" }
val email = authStorage.getEmail() ?: return
val password = authStorage.getPassword() ?: return
runCatchingExceptCancel { authenticate(email, password) }
.onFailure { logout() } // Clear all known values to avoid reusing them
}
companion object {
private const val AUTH_HEADER_FORMAT = "Bearer %s"
private const val API_TOKEN_NAME = "Mealient"
}
}

View File

@@ -32,14 +32,6 @@ class AuthStorageImpl @Inject constructor(
override suspend fun getAuthHeader(): String? = getString(AUTH_HEADER_KEY)
override suspend fun setEmail(email: String?) = putString(EMAIL_KEY, email)
override suspend fun getEmail(): String? = getString(EMAIL_KEY)
override suspend fun setPassword(password: String?) = putString(PASSWORD_KEY, password)
override suspend fun getPassword(): String? = getString(PASSWORD_KEY)
private suspend fun putString(
key: String,
value: String?
@@ -57,11 +49,5 @@ class AuthStorageImpl @Inject constructor(
companion object {
@VisibleForTesting
const val AUTH_HEADER_KEY = "authHeader"
@VisibleForTesting
const val EMAIL_KEY = "email"
@VisibleForTesting
const val PASSWORD_KEY = "password"
}
}