Skip to content

Commit

Permalink
fix auth token barrier
Browse files Browse the repository at this point in the history
  • Loading branch information
stslex committed Aug 7, 2023
1 parent 3b75406 commit b133a88
Show file tree
Hide file tree
Showing 17 changed files with 309 additions and 150 deletions.
11 changes: 10 additions & 1 deletion app/src/main/java/com/stslex/aproselection/SelectApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@ package com.stslex.aproselection

import android.app.Application
import com.stslex.aproselection.controller.AuthController
import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.datastore.coreDataStoreModule
import com.stslex.aproselection.core.network.di.ModuleCoreNetwork.moduleCoreNetwork
import com.stslex.aproselection.core.user.di.moduleCoreUser
import com.stslex.aproselection.di.appModule
import com.stslex.aproselection.feature.auth.di.ModuleFeatureAuth.moduleFeatureAuth
import com.stslex.aproselection.feature.home.di.moduleFeatureHome
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin

class SelectApplication : Application() {

private val coroutineScope = CoroutineScope(SupervisorJob())
private val appController: AuthController by inject()
private val dataStore: AppDataStore by inject()

override fun onCreate() {
super.onCreate()
Expand All @@ -24,7 +30,10 @@ class SelectApplication : Application() {
}

private fun initControllers() {
appController.init()
coroutineScope.launch {
appController.init()
dataStore.init()
}
}

private fun setupDependencies() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ interface AuthController {

val isAuth: StateFlow<Boolean?>

fun init()
suspend fun init()
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,36 @@ package com.stslex.aproselection.controller

import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.network.client.NetworkClient
import com.stslex.aproselection.core.core.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.coroutineContext

class AuthControllerImpl(
private val dataStore: AppDataStore,
private val networkClient: NetworkClient,
) : AuthController {

private val scope = CoroutineScope(SupervisorJob())
private val _isAuth = MutableStateFlow<Boolean?>(null)
override val isAuth: StateFlow<Boolean?>
get() = _isAuth.asStateFlow()

override fun init() {
override suspend fun init() {
dataStore.token
.catch { error ->
Logger.exception(error)
}
.onEach { token ->
if (token.isBlank()) {
networkClient.regenerateToken()
}
}
.launchIn(scope)
.launchIn(CoroutineScope(coroutineContext))

dataStore.uuid
.catch { error ->
Logger.exception(error)
}
.onEach { uuid ->
_isAuth.emit(uuid.isNotBlank())
}
.launchIn(scope)
.launchIn(CoroutineScope(coroutineContext))
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.stslex.aproselection.core.datastore

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

interface AppDataStore {

val uuid: Flow<String>
val token: Flow<String>
val uuid: StateFlow<String>
val token: StateFlow<String>

suspend fun setUuid(uuid: String)

suspend fun setToken(token: String)

suspend fun clear()

suspend fun init()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.stslex.aproselection.core.core.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlin.coroutines.coroutineContext

class AppDataStoreImpl(
private val context: Context
Expand All @@ -21,19 +28,13 @@ class AppDataStoreImpl(
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(DATA_STORE_KEY)
}

override val uuid: Flow<String>
get() = context.dataStore.data
.map { prefs ->
prefs[UUID_KEY].orEmpty()
}
.flowOn(Dispatchers.IO)
private val _uuid = MutableStateFlow("")
override val uuid: StateFlow<String>
get() = _uuid.asStateFlow()

override val token: Flow<String>
get() = context.dataStore.data
.map { prefs ->
prefs[TOKEN_KEY].orEmpty()
}
.flowOn(Dispatchers.IO)
private val _token = MutableStateFlow("")
override val token: StateFlow<String>
get() = _token.asStateFlow()

override suspend fun setUuid(uuid: String) {
context.dataStore.updateData { prefs ->
Expand All @@ -51,6 +52,19 @@ class AppDataStoreImpl(
}
}

override suspend fun init() {
context.dataStore.data
.catch { error ->
Logger.exception(error)
}
.onEach { prefs ->
_uuid.value = prefs[UUID_KEY].orEmpty()
_token.value = prefs[TOKEN_KEY].orEmpty()
}
.flowOn(Dispatchers.IO)
.launchIn(CoroutineScope(coroutineContext))
}

override suspend fun clear() {
setUuid("")
setToken("")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import io.ktor.client.HttpClient

interface NetworkClient {

val client: HttpClient

val apiClient: HttpClient

suspend fun regenerateToken()
suspend fun regenerateToken(): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,97 +12,81 @@ import io.ktor.client.call.body
import io.ktor.client.engine.android.Android
import io.ktor.client.plugins.HttpResponseValidator
import io.ktor.client.plugins.ResponseException
import io.ktor.client.plugins.auth.Auth
import io.ktor.client.plugins.auth.providers.BearerTokens
import io.ktor.client.plugins.auth.providers.bearer
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.DEFAULT
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.HttpRequest
import io.ktor.client.request.bearerAuth
import io.ktor.client.request.get
import io.ktor.http.ContentType
import io.ktor.http.URLProtocol
import io.ktor.http.appendPathSegments
import io.ktor.http.contentType
import io.ktor.http.encodedPath
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json

class NetworkClientImpl(
private val dataStore: AppDataStore
) : NetworkClient {

private val scope = CoroutineScope(SupervisorJob())
private val _token = MutableStateFlow("")
private val token = _token.asStateFlow()
private val _uuid = MutableStateFlow("")
private val uuid = _uuid.asStateFlow()

// TODO (вынести из init блока)
init {
dataStore.token
.onEach {
_token.emit(it)
}
.launchIn(scope)

dataStore.uuid
.onEach {
_uuid.emit(it)
}
.launchIn(scope)
}

@OptIn(ExperimentalSerializationApi::class)
override val client: HttpClient
get() = HttpClient(Android) {
private val client: HttpClient = HttpClient(Android) {
expectSuccess = true

expectSuccess = true
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}

install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
install(Auth) {
bearer {
loadTokens {
val token = dataStore.token.value
BearerTokens(token, token)
}
refreshTokens {
val token = regenerateToken()
BearerTokens(token, token)
}
}
}

install(ContentNegotiation) {
json(
Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
explicitNulls = false
}
)
}
install(ContentNegotiation) {
json(
Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
explicitNulls = false
}
)
}

HttpResponseValidator {
handleResponseExceptionWithRequest(errorParser)
}
HttpResponseValidator {
handleResponseExceptionWithRequest(errorParser)
}
}

override val apiClient: HttpClient
get() = client.config {
defaultRequest {
url {
host = BuildConfig.API_SERVER_HOST
encodedPath = BuildConfig.API_VERSION
protocol = URLProtocol.HTTP
headers.append("API_KEY", BuildConfig.API_KEY)
headers.append("DEVICE_ID", "test")
headers.append("uuid", uuid.value)
contentType(ContentType.Application.Json)
}
bearerAuth(token = token.value)
override val apiClient: HttpClient = client.config {
defaultRequest {
url {
host = BuildConfig.API_SERVER_HOST
encodedPath = BuildConfig.API_VERSION
protocol = URLProtocol.HTTP
headers.append("API_KEY", BuildConfig.API_KEY)
headers.append("DEVICE_ID", "test")
headers.append("uuid", dataStore.uuid.value)
contentType(ContentType.Application.Json)
}
}
}

private val errorParser: suspend (Throwable, HttpRequest) -> Unit
get() = { exception, _ ->
Expand All @@ -123,13 +107,14 @@ class NetworkClientImpl(
}
}

override suspend fun regenerateToken() {
override suspend fun regenerateToken(): String {
val token = apiClient
.get {
url.appendPathSegments("token")
}
.body<TokenResponse>()
.token
dataStore.setToken(token)
return token
}
}
Loading

0 comments on commit b133a88

Please sign in to comment.