Как работают refresh токены и как они получаются и обновляются
Что проверяют
Понимаете ли вы, зачем токенов два и чем refresh token опаснее access token. Следом почти всегда спрашивают, как отозвать сессию и что будет, если refresh token украдут.
Как отвечать
Опишите цикл по OAuth 2.0:
- выдача: после входа token endpoint возвращает короткий access token (минуты) и долгий refresh token (дни или недели). В OpenID Connect refresh token обычно выдаётся по scope
offline_access; - обновление: когда access token истёк, клиент шлёт
POSTна token endpoint сgrant_type=refresh_tokenи получает новую пару. Пароль второй раз не нужен; - ротация: при каждом обновлении старый refresh token гасится и выдаётся новый. Если погашенный токен приходит повторно, это признак кражи, и сервер отзывает всю цепочку сессии;
- хранение на сервере: refresh token лучше делать непрозрачной случайной строкой и держать в базе в виде хэша вместе с пользователем, устройством и сроком. Тогда отзыв это просто удаление записи;
- хранение на клиенте: в браузере cookie с
HttpOnly,SecureиSameSite, на мобильных системное защищённое хранилище.
В Java-стеке сервер авторизации обычно берут готовый: Spring Authorization Server или Keycloak. В Spring Authorization Server сроки и ротация задаются в TokenSettings: refreshTokenTimeToLive, reuseRefreshTokens(false). Сервис с ресурсами проверяет access token в виде JWT локально по подписи через spring-boot-starter-oauth2-resource-server, без похода в базу. Поэтому его и делают коротким: отозвать JWT до истечения срока нельзя.
Где спотыкаются
Делают refresh token таким же JWT и проверяют только подпись. Такой токен нельзя отозвать при выходе или смене пароля, пока он не истечёт сам.
Отправляют refresh token с каждым запросом к API. Он должен ходить только на token endpoint, иначе поверхность утечки та же, что у access token.
Забывают про гонку: несколько параллельных запросов одновременно видят истёкший access token и обновляют его разом. При ротации второй запрос приходит с уже погашенным токеном и выбивает пользователя. Нужна одна общая операция обновления на клиенте, остальные запросы её ждут.