JWT Authorization

This commit is contained in:
Koitharu
2022-05-03 17:47:27 +03:00
parent 00e1aac984
commit fbc86f6d3b
5 changed files with 78 additions and 26 deletions

View File

@@ -3,10 +3,13 @@ package org.koitharu.kotatsu.sync
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.sync.data.SyncAuthApi
import org.koitharu.kotatsu.sync.ui.SyncAuthViewModel
val syncModule
get() = module {
viewModel { SyncAuthViewModel(androidContext(), get()) }
factory { SyncAuthApi(androidContext(), get()) }
viewModel { SyncAuthViewModel(get()) }
}

View File

@@ -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()
}

View File

@@ -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")
}
}

View File

@@ -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.parsers.util.json.mapJSONTo
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.SyncAuthApi
import org.koitharu.kotatsu.utils.GZipInterceptor
import org.koitharu.kotatsu.utils.ext.toContentValues
import org.koitharu.kotatsu.utils.ext.toJson
@@ -40,6 +42,7 @@ class SyncHelper(
) {
private val httpClient = OkHttpClient.Builder()
.authenticator(AccountAuthenticator(context, account, SyncAuthApi(context, OkHttpClient())))
.addInterceptor(AccountInterceptor(context, account))
.addInterceptor(GZipInterceptor())
.build()

View File

@@ -1,44 +1,22 @@
package org.koitharu.kotatsu.sync.ui
import android.content.Context
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.parsers.util.await
import org.koitharu.kotatsu.parsers.util.parseJson
import org.koitharu.kotatsu.sync.data.SyncAuthApi
import org.koitharu.kotatsu.sync.domain.SyncAuthResult
import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.toRequestBody
import java.util.*
class SyncAuthViewModel(
context: Context,
private val okHttpClient: OkHttpClient,
private val api: SyncAuthApi,
) : BaseViewModel() {
private val baseUrl = context.getString(R.string.url_sync_server)
val onTokenObtained = SingleLiveEvent<SyncAuthResult>()
fun obtainToken(email: String, password: String) {
launchLoadingJob(Dispatchers.Default) {
authenticate(email, password)
val token = UUID.randomUUID().toString()
val token = api.authenticate(email, password)
val result = SyncAuthResult(email, password, token)
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()
}
}