Может ли Retrofit помочь с просроченным токеном сделать запрос

Ответ

Retrofit сам по себе не обрабатывает просроченные токены, но можно использовать Interceptor для автоматического обновления токена при 401 ошибке.

Пример:

class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer ${tokenManager.getToken()}")
            .build()

        val response = chain.proceed(request)

        if (response.code == 401) {
            val newToken = tokenManager.refreshToken() // синхронный запрос
            val newRequest = request.newBuilder()
                .header("Authorization", "Bearer $newToken")
                .build()
            return chain.proceed(newRequest)
        }
        return response
    }
}

Важно:

  • Обновление токена должно быть синхронным внутри intercept().
  • Учитывайте возможные рекурсии (если refresh-токен тоже протух).
  • Лучше добавить механизм блокировки параллельных обновлений токена.

Ответ 18+ 🔞

А, Retrofit, ёпта! Ну классика же. Сам он, конечно, как мартышлюшка с гранатой — в руки дали, а он нихуя не понимает, когда токен сдох. Надо ему всё на блюдечке с голубой каёмочкой принести.

Смотри, вот тебе простой рецепт, как эту манду с ушами заставить работать. Берёшь Interceptor, и там уже делаешь свою магию.

Вот смотри, пример кода, тут всё правильно:

class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer ${tokenManager.getToken()}")
            .build()

        val response = chain.proceed(request)

        if (response.code == 401) {
            val newToken = tokenManager.refreshToken() // синхронный запрос
            val newRequest = request.newBuilder()
                .header("Authorization", "Bearer $newToken")
                .build()
            return chain.proceed(newRequest)
        }
        return response
    }
}

Выглядит вроде логично, да? Но тут, чувак, подозрение ебать чувствую, что сейчас накосячим. Запомни три вещи, а то будет тебе хиросима и нигасраки, а не авторизация.

Во-первых, этот refreshToken() — он должен быть синхронным, внутри intercept(). Нельзя тут корутины или колбэки городить, потому что метод intercept должен вернуть Response, а не твои хотелки. Если пойдёшь асинхронным путём — получишь хуй в пальто вместо ответа.

Во-вторых, представь ситуацию: пришёл запрос, токен протух. Ты пошёл обновлять, а refresh-токен-то тоже, сука, просрочен! Или сервер тебе говорит: «Иди нахуй, недействительный». Получается рекурсия — 401, обновляю, снова 401, снова обновляю... До бесконечности. Надо чётко это обрабатывать, а то уйдёшь в бесконечный цикл и накроешься медным тазом.

И в-третьих, самое жирное. Допустим, у тебя пять запросов полетели одновременно, и у всех токен сдох. Что делает твой код? Каждый поток, получив 401, побежит обновлять токен. Получится овердохуища одинаковых запросов на /refresh, сервер обоссытся, а ты получишь кучу ошибок. Надо ставить замок (synchronized или Mutex), чтобы пока один поток токен обновляет, остальные ждали, а не лезли как гомосеки налетели.

Короче, идея простая, но деталей — ядрёна вошь. Без них получится не решение, а пиздопроебибна.