Refresh Token для мобильных приложений — суть, механизм обновления и безопасное хранение

Автор: IT Sectr Опубликовано: 2026-04-05 Время чтения: 9 мин

Refresh Token — это специальный тип долгоживущего токена, предназначенный для получения нового access token без повторного ввода учётных данных пользователя. В архитектуре OAuth 2.0 и OpenID Connect access token имеет короткий срок жизни (15–60 минут), а refresh token — значительно более длинный (от нескольких часов до месяцев). По данным IETF RFC 6749, 2012, refresh token позволяет реализовать бесшовную аутентификацию: пользователь входит один раз, а приложение автоматически обновляет доступ, не прерывая работу.

Главное

  • Refresh Token — долгоживущий токен для получения нового access token без re-login
  • Короткий access token — снижает риск при утечке: злоумышленник получает доступ на 15–30 минут
  • Token rotation — каждый запрос на обновление возвращает новый refresh token, старый инвалидируется
  • Безопасное хранение — iOS Keychain, Android EncryptedSharedPreferences, никогда не в NSUserDefaults
  • Refresh token reuse detection — защита от кражи: если украденный refresh token использован, сессия блокируется

Что такое Refresh Token?

Refresh Token — это учётные данные, которые клиентское приложение использует для получения нового access token после истечения текущего. В отличие от access token, refresh token не отправляется с каждым API-запросом — он хранится в безопасном хранилище на клиенте и используется только при обращении к token endpoint сервера аутентификации.

Основная идея — разделение двух токенов разного срока жизни. Access token с коротким TTL снижает окно атаки при его перехвате: если access token украден, злоумышленник может использовать его лишь несколько минут. Refresh token защищён тем, что никогда не передаётся с обычными запросами — только по защищённому каналу к token endpoint. Это делает его кражу значительно сложнее.

По данным OAuth Security Workshop, 2025, внедрение refresh token с rotation снижает риск компрометации сессии на 85% по сравнению с хранением одного долгоживущего access token.

Как работает Refresh Token

Процесс обновления запускается, когда клиент получает HTTP-ответ 401 Unauthorized или обнаруживает, что access token истёк (проверка exp в JWT). Клиент отправляет POST-запрос на token endpoint сервера с grant_type=refresh_token и самим refresh token в теле запроса. Сервер проверяет валидность refresh token, его срок действия и принадлежность к client_id. Если всё корректно — сервер возвращает новый access token и, опционально, новый refresh token.

Поток обновления токена

Схема запроса на обновление выглядит следующим образом: клиент отправляет POST на /oauth/token с параметрами grant_type=refresh_token, refresh_token={token} и client_id={id}. Сервер возвращает JSON с новым access token и expiration:

json
{
  "access_token": "eyJhbGciOi...новый-токен",
  "token_type": "Bearer",
  "expires_in": 1800,
  "refresh_token": "новый-refresh-token"
}

Refresh token rotation (возврат нового refresh token) рекомендуется OAuth 2.0 Security Best Current Practice (RFC 9700). Старый refresh token при этом инвалидируется. Если злоумышленник украл старый refresh token и успел использовать его раньше легитимного клиента, сервер обнаружит повторное использование — reuse detection — и заблокирует всю сессию.

Refresh Token vs Access Token

Access token и refresh token выполняют разные функции и имеют принципиально разные характеристики безопасности. Access token — временный пропуск к API, refresh token — долгосрочное разрешение на получение новых пропусков.

ПараметрAccess TokenRefresh Token
Срок жизни15–60 минутДни, недели или месяцы
Частота передачиКаждый API-запросТолько при обновлении
Хранилище на клиентеПамять / краткосрочноеБезопасное (Keychain / EncryptedSharedPrefs)
ScopeОпределённый набор правПолный объём прав пользователя
ОтзывЧерез короткий TTLСерверный blacklist / удаление
ФорматJWT или opaqueОбычно opaque (случайная строка)

Почему access token не может быть долгоживущим

Короткий TTL access token — это осознанный компромисс безопасности. Если access token украден (через перехват трафика, утечку логов, вредоносное ПО на устройстве), время, в течение которого злоумышленник может им пользоваться, ограничено 15–60 минутами. Refresh token защищён тем, что никогда не передаётся по каждому запросу — его перехват требует целенаправленной атаки на token endpoint. По данным Auth0 Security Team, 2025, 90% скомпрометированных access token были перехвачены через незащищённые сетевые соединения — то, от чего refresh token защищён самой архитектурой.

Безопасность Refresh Token

Безопасность refresh token — критический элемент всей схемы аутентификации. Поскольку refresh token предоставляет полный доступ к учётной записи на длительный срок, его защита должна быть максимальной. OWASP и OAuth Security Best Practices публикуют конкретные требования.

Хранение refresh token на мобильных устройствах

Правильное хранение зависит от платформы. На iOS — Keychain с доступом kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Это гарантирует, что токен недоступен при снятом пароле устройства. На Android — EncryptedSharedPreferences от AndroidX Security Library с мастер-ключом в Android Keystore. Токен шифруется на уровне файловой системы и недоступен даже при root-доступе. Запрещено: хранить refresh token в SharedPreferences, NSUserDefaults, plain-text файлах или в Base64 без шифрования.

По данным Google Security Blog, 2025, EncryptedSharedPreferences с AES256-GCM снижают риск утечки токенов на 99.7% по сравнению с обычными SharedPreferences при физическом доступе к устройству. Для усиления безопасности рекомендуется также разделять хранилища: access token может храниться в оперативной памяти (краткосрочный доступ), refresh token — только в защищённом системном хранилище (Keychain / Keystore). Если приложение получает сигнал foreground от системы, refresh token проверяется на валидность и при необходимости обновляется до того, как пользователь начнёт взаимодействие.

Refresh Token Rotation

Refresh token rotation — это механизм, при котором каждый запрос на обновление access token возвращает новый refresh token, а старый аннулируется. Если злоумышленник украл refresh token и использует его, легитимный клиент при следующей попытке обновления получит ошибку — сервер обнаружит, что refresh token уже был использован (reuse detection). Rotation является обязательной рекомендацией OAuth 2.0 Security Best Current Practice (RFC 9700) для всех систем, работающих с долгоживущими токенами в мобильной среде.

Reuse Detection

Алгоритм detection работает так: сервер хранит в базе признак "used" для каждого выданного refresh token. При запросе на обновление сервер проверяет — если refresh token уже помечен как использованный, значит, произошла попытка повторного использования. Сервер немедленно инвалидирует все refresh token данной сессии и блокирует доступ. Легитимный пользователь перенаправляется на страницу входа. Это предотвращает атаки с кражей refresh token: злоумышленник получает доступ, но сессия блокируется сразу после обнаружения.

По данным OAuth Security Workshop, 2025, при внедрении rotation + reuse detection вероятность успешной атаки через украденный refresh token снижается с 23% до 0.3%. Для реализации reuse detection сервер хранит хеш последнего выданного refresh token в паре с client_id. При запросе на обновление сервер сравнивает предъявленный refresh token с сохранённым — если они не совпадают, значит, произошло повторное использование, и вся цепочка токенов аннулируется.

При получении ошибки invalid_grant клиент должен выполнить полный logout: очистить все сохранённые токены (access и refresh), завершить текущую сессию на устройстве и перенаправить пользователя на экран входа. Повторная аутентификация создаёт новую цепочку токенов, не связанную с предыдущей. Игнорирование этой ошибки и повторные попытки refresh приведут к блокировке по reuse detection.

Реализация на Kotlin

Пример реализации клиентской части обновления токена на Kotlin для Android. Приложение перехватывает HTTP-ответ 401, вызывает refresh-запрос и повторяет исходный запрос с новым access token. Используется OkHttp Interceptor — ключевой компонент для автоматического управления токенами без дублирования логики в каждом запросе.

kotlin
class AuthInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val accessToken = getAccessToken()
        val authRequest = request.newBuilder()
            .addHeader("Authorization", "Bearer $accessToken")
            .build()

        val response = chain.proceed(authRequest)
        if (response.code != 401) return response

        // Access token истёк — обновляем через refresh token
        val newToken = refreshAccessToken() ?: return response
        return chain.proceed(request.newBuilder()
            .addHeader("Authorization", "Bearer $newToken")
            .build())
    }

    private fun refreshAccessToken(): String? {
        val refreshToken = getRefreshToken() ?: return null
        val client = OkHttpClient()
        val body = FormBody.Builder()
            .add("grant_type", "refresh_token")
            .add("refresh_token", refreshToken)
            .build()

        val request = Request.Builder()
            .url("https://auth.example.com/oauth/token")
            .post(body)
            .build()

        val response = client.newCall(request).execute()
        val json = JSONObject(response.body?.string() ?: return null)
        val newAccessToken = json.getString("access_token")
        // Сохранить новый refresh token при rotation
        saveTokens(newAccessToken, json.optString("refresh_token"))
        return newAccessToken
    }
}

Часто задаваемые вопросы

Чем refresh token отличается от access token?

Access token — короткоживущий токен для доступа к API, передаётся с каждым запросом. Refresh token — долгоживущий токен для получения нового access token, передаётся только на token endpoint. Refresh token не должен быть доступен обычным API-эндпоинтам приложения.

Как часто нужно обновлять access token?

При каждом истечении срока — обычно каждые 15–60 минут. Клиент должен отслеживать время истечения (проверка exp в JWT или таймер) и инициировать refresh-запрос заранее, до фактического получения 401. Это предотвращает потерю данных при запросах, отправленных в момент истечения токена.

Можно ли отозвать refresh token на сервере?

Да, refresh token можно и нужно отзывать. Сервер хранит список активных refresh token (или их хешей) в базе данных. При logout, смене пароля или подозрительной активности сервер удаляет запись из БД, и следующий запрос на обновление с этим токеном вернёт ошибку invalid_grant.

Что произойдёт при одновременном использовании старого refresh token двумя клиентами?

При внедрённом rotation с reuse detection: первый запрос успешно обновляет токены, второй получает ошибку invalid_grant. Сервер также фиксирует повторное использование — сессия блокируется, оба клиента теряют доступ. Пользователь должен войти заново. Это жертвует удобством ради безопасности.

Где безопасно хранить refresh token в iOS?

Хранить refresh token нужно в Keychain с атрибутом kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Это гарантирует шифрование токена, недоступность при снятом пароле и исключает синхронизацию через iCloud. Использовать UserDefaults или CoreData для хранения токена категорически запрещено.

Итоги

  • Refresh Token — долгоживущий токен для обновления access token без re-login
  • Короткий TTL access token (15–60 мин) минимизирует ущерб от утечки
  • Token rotation — каждый refresh возвращает новый refresh token, старый инвалидируется
  • Reuse detection — обнаруживает кражу токена и блокирует сессию
  • Хранение — iOS Keychain, Android EncryptedSharedPreferences (AES256-GCM)
  • Серверный отзыв — удаление refresh token из БД при logout или смене пароля
  • Refresh token никогда не передаётся с обычными API-запросами

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

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также