Невозможно использовать GoogleSignInClient.silentSignIn () для получения токена идентификатора в аутентификаторе OkHttpClient. - PullRequest
0 голосов
/ 07 апреля 2020

В моем приложении Android, написанном на Kotlin, я использую Google Sign-In для авторизации и токены JWT для аутентификации с помощью моего внутреннего API. Срок действия моих токенов JWT и токена JWT в Google Sign-In истекает через 1 час.

Я хочу, чтобы мое приложение повторно авторизировалось с помощью Google Sign-In и повторно аутентифицировалось с моим API при запросе к моему внутреннему API завершается с ошибкой 401, поэтому я добавил аутентификатор в OkHttpClient.

Однако, когда я запускаю приведенный ниже код, задача silentSignIn навсегда блокируется и не отвечает в строке val result = Tasks.await(task).

Я что-то упускаю из виду? Я не могу найти причину того, почему silentSignIn () блокируется при вызове из аутентификатора.

В других местах я не использую Tasks.await(task), так как там я использую OnCompleteListener для обновления Пользовательский интерфейс, обновите переменные apiToken и googleToken, однако в приведенном ниже коде я хотел обновить apiToken и googleToken, когда запрос не прошел, не требуя, чтобы другой код в моем проекте перезапустил запрос на API бэкэнда.

TLDR: я хочу вызвать GoogleSignIn.silentSignIn () в AutHenticator OkHttpClient, получить токен и немедленно повторить запрос, но мое приложение зависает на Tasks.await.

    val RC_SIGN_IN = 9001
    var apiToken: String? = null
    var googleToken: String? = null

    val client: OkHttpClient = OkHttpClient
        .Builder()
        .authenticator(object : Authenticator {
            override fun authenticate(route: Route?, response: Response): Request? {
                return if (response.code == 401) {
                    // Configure sign-in to request the user's ID, email address, and basic
                    // profile. ID and basic profile are included in DEFAULT_SIGN_IN.
                    val gso =
                        GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                            .requestEmail()
                            .requestIdToken("token_goes_here")
                            .build()

                    // Build a GoogleApiClient with access to the Google Sign-In API and the
                    // options specified by gso.
                    val googleSignInClient = GoogleSignIn.getClient(this@MainActivity, gso)
                    val task = googleSignInClient.silentSignIn().addOnCompleteListener {
                        val result = it.getResult(ApiException::class.java)
                        googleToken = result?.idToken
                    }.addOnFailureListener {
                        val signInIntent = googleSignInClient.signInIntent
                        startActivityForResult(signInIntent, RC_SIGN_IN)
                    }

                    if (task.isSuccessful) {
                        googleToken = task.result?.idToken
                    } else {
                        try {
                            /**** The following line is where the routine locks up: ****/
                            val result = Tasks.await(task)
                            googleToken = result?.idToken
                        } catch (ex: ExecutionException) {
                            // task failed
                            Log.e(TAG, "authenticate error:{ex}")
                        } catch (ex: InterruptedException) {
                            // an interrupt occurred while waiting for the task to finish
                            Log.e(TAG, "authenticate error:{ex}")
                        }
                    }
                    apiToken = getApiToken(googleToken)
                    response.request
                        .newBuilder()
                        .header("Authorization", "Bearer $apiToken")
                        .build()
                } else {
                    null
                }
            }
        }
        )
        .build()

1 Ответ

0 голосов
/ 14 апреля 2020

После некоторых копаний я нашел ответ. Ради кого-то еще в подобной лодке я опишу проблему и решение ниже:

Мои подозрения возросли, когда у меня начиналась похожая ошибка с библиотекой биллинга Google Play, блокирующейся подобным образом , Расследование показало, что я вызывал код API get () из контекста runBlocking {}. Google использовал тот же поток, что и OkHTTP, и поэтому runBlocking приводил к блокировке всех сетевых операций ввода-вывода за пределами OkHTTP до тех пор, пока OkHTTP не был завершен.

Решением было удаление везде, где это возможно, runBlocking, загрузка данных через запуск {} асинхронно и используйте notifyDataSetChanged (), чтобы обновить RecyclerView, когда это будет сделано. Я не смог полностью удалить runBlocking, но сейчас он так мало используется, и я переработал блок Task.await (), чтобы он отключился, и вызвал намерение по таймауту:

try {
    val result = Tasks.await(task, 5, TimeUnit.SECONDS)
    googleToken = result?.idToken
} catch (ex: ExecutionException) {
    // task failed
    Log.e(TAG, "authenticate error:{ex}")
    val signInIntent = googleSignInClient.signInIntent
    startActivityForResult(signInIntent, RC_SIGN_IN)
} catch (ex: InterruptedException) {
    // an interrupt occurred while waiting for the task to finish
    Log.e(TAG, "authenticate error:{ex}")
    val signInIntent = googleSignInClient.signInIntent
    startActivityForResult(signInIntent, RC_SIGN_IN)
} catch (ex: TimeoutException) {
    // an timeout occurred while waiting for the task to finish
    Log.e(TAG, "authenticate error:{ex}")
    val signInIntent = googleSignInClient.signInIntent
    startActivityForResult(signInIntent, RC_SIGN_IN)
}

Лучшим решением было бы реализовать ViewModels (https://developer.android.com/topic/libraries/architecture/viewmodel), который я рекомендую для нового проекта, но из-за сжатых сроков я не смог сделать это в этом выпуске.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...