JWT Authorization
This commit is contained in:
@@ -3,10 +3,13 @@ package org.koitharu.kotatsu.sync
|
|||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
import org.koitharu.kotatsu.sync.data.SyncAuthApi
|
||||||
import org.koitharu.kotatsu.sync.ui.SyncAuthViewModel
|
import org.koitharu.kotatsu.sync.ui.SyncAuthViewModel
|
||||||
|
|
||||||
val syncModule
|
val syncModule
|
||||||
get() = module {
|
get() = module {
|
||||||
|
|
||||||
viewModel { SyncAuthViewModel(androidContext(), get()) }
|
factory { SyncAuthApi(androidContext(), get()) }
|
||||||
|
|
||||||
|
viewModel { SyncAuthViewModel(get()) }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.koitharu.kotatsu.sync.data
|
||||||
|
|
||||||
|
import android.accounts.Account
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import android.content.Context
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.Authenticator
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.Route
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
|
||||||
|
class AccountAuthenticator(
|
||||||
|
context: Context,
|
||||||
|
private val account: Account,
|
||||||
|
private val authApi: SyncAuthApi,
|
||||||
|
) : Authenticator {
|
||||||
|
|
||||||
|
private val accountManager = AccountManager.get(context)
|
||||||
|
private val tokenType = context.getString(R.string.account_type_sync)
|
||||||
|
|
||||||
|
override fun authenticate(route: Route?, response: Response): Request? {
|
||||||
|
val newToken = tryRefreshToken() ?: return null
|
||||||
|
accountManager.setAuthToken(account, tokenType, newToken)
|
||||||
|
return response.request.newBuilder()
|
||||||
|
.header("Authorization", "Bearer $newToken")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryRefreshToken() = runCatching {
|
||||||
|
runBlocking {
|
||||||
|
authApi.authenticate(
|
||||||
|
account.name,
|
||||||
|
accountManager.getPassword(account),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.koitharu.kotatsu.sync.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.parsers.util.await
|
||||||
|
import org.koitharu.kotatsu.parsers.util.parseJson
|
||||||
|
import org.koitharu.kotatsu.utils.ext.toRequestBody
|
||||||
|
|
||||||
|
class SyncAuthApi(
|
||||||
|
context: Context,
|
||||||
|
private val okHttpClient: OkHttpClient,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val baseUrl = context.getString(R.string.url_sync_server)
|
||||||
|
|
||||||
|
suspend fun authenticate(email: String, password: String): String {
|
||||||
|
val body = JSONObject(
|
||||||
|
mapOf("email" to email, "password" to password)
|
||||||
|
).toRequestBody()
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("$baseUrl/auth")
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
val response = okHttpClient.newCall(request).await().parseJson()
|
||||||
|
return response.getString("token")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,9 @@ import org.koitharu.kotatsu.core.db.MangaDatabase.Companion.TABLE_MANGA_TAGS
|
|||||||
import org.koitharu.kotatsu.core.db.MangaDatabase.Companion.TABLE_TAGS
|
import org.koitharu.kotatsu.core.db.MangaDatabase.Companion.TABLE_TAGS
|
||||||
import org.koitharu.kotatsu.parsers.util.json.mapJSONTo
|
import org.koitharu.kotatsu.parsers.util.json.mapJSONTo
|
||||||
import org.koitharu.kotatsu.parsers.util.parseJson
|
import org.koitharu.kotatsu.parsers.util.parseJson
|
||||||
|
import org.koitharu.kotatsu.sync.data.AccountAuthenticator
|
||||||
import org.koitharu.kotatsu.sync.data.AccountInterceptor
|
import org.koitharu.kotatsu.sync.data.AccountInterceptor
|
||||||
|
import org.koitharu.kotatsu.sync.data.SyncAuthApi
|
||||||
import org.koitharu.kotatsu.utils.GZipInterceptor
|
import org.koitharu.kotatsu.utils.GZipInterceptor
|
||||||
import org.koitharu.kotatsu.utils.ext.toContentValues
|
import org.koitharu.kotatsu.utils.ext.toContentValues
|
||||||
import org.koitharu.kotatsu.utils.ext.toJson
|
import org.koitharu.kotatsu.utils.ext.toJson
|
||||||
@@ -40,6 +42,7 @@ class SyncHelper(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
private val httpClient = OkHttpClient.Builder()
|
private val httpClient = OkHttpClient.Builder()
|
||||||
|
.authenticator(AccountAuthenticator(context, account, SyncAuthApi(context, OkHttpClient())))
|
||||||
.addInterceptor(AccountInterceptor(context, account))
|
.addInterceptor(AccountInterceptor(context, account))
|
||||||
.addInterceptor(GZipInterceptor())
|
.addInterceptor(GZipInterceptor())
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -1,44 +1,22 @@
|
|||||||
package org.koitharu.kotatsu.sync.ui
|
package org.koitharu.kotatsu.sync.ui
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import org.json.JSONObject
|
|
||||||
import org.koitharu.kotatsu.R
|
|
||||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.parsers.util.await
|
import org.koitharu.kotatsu.sync.data.SyncAuthApi
|
||||||
import org.koitharu.kotatsu.parsers.util.parseJson
|
|
||||||
import org.koitharu.kotatsu.sync.domain.SyncAuthResult
|
import org.koitharu.kotatsu.sync.domain.SyncAuthResult
|
||||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||||
import org.koitharu.kotatsu.utils.ext.toRequestBody
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class SyncAuthViewModel(
|
class SyncAuthViewModel(
|
||||||
context: Context,
|
private val api: SyncAuthApi,
|
||||||
private val okHttpClient: OkHttpClient,
|
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val baseUrl = context.getString(R.string.url_sync_server)
|
|
||||||
val onTokenObtained = SingleLiveEvent<SyncAuthResult>()
|
val onTokenObtained = SingleLiveEvent<SyncAuthResult>()
|
||||||
|
|
||||||
fun obtainToken(email: String, password: String) {
|
fun obtainToken(email: String, password: String) {
|
||||||
launchLoadingJob(Dispatchers.Default) {
|
launchLoadingJob(Dispatchers.Default) {
|
||||||
authenticate(email, password)
|
val token = api.authenticate(email, password)
|
||||||
val token = UUID.randomUUID().toString()
|
|
||||||
val result = SyncAuthResult(email, password, token)
|
val result = SyncAuthResult(email, password, token)
|
||||||
onTokenObtained.postCall(result)
|
onTokenObtained.postCall(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun authenticate(email: String, password: String) {
|
|
||||||
val body = JSONObject(
|
|
||||||
mapOf("email" to email, "password" to password)
|
|
||||||
).toRequestBody()
|
|
||||||
val request = Request.Builder()
|
|
||||||
.url("$baseUrl/register")
|
|
||||||
.post(body)
|
|
||||||
.build()
|
|
||||||
val response = okHttpClient.newCall(request).await().parseJson()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user