@lexstile

Как реализовать авторизацию/аутентификацию с помощью access/refresh tokens с использованием JWT?

Пожалуйста, объясните ключевые особенности работы.
Я понимаю, что access token нужен для аутентификации запросов, живет 15-30 мин.
Я понимаю, что refresh token нужен для продления access token, живет гораздо больше, например 30 дней.

Я не понимаю:
1. Допустим, мы логинимся, создаем 2 токена - access и refresh, последний записываем в специальную таблицу сессий в БД, верно? (но не совсем понятно, что храним, на ум приходит только срок жизни)
Если записать сигнатуру - мы ее и так можем получить, используя данные пользователя.
Записать user agent - а если пользователь обновит браузер?
IP - а если сменит wi-fi или переключится на SIM?
2. Куда записываем эти токены? Кто-то говорит access пишем в cookie httpOnly, кто-то говорит, что туда пишем refresh.
А можем, допустим, оба туда записать? (Пример - пользователь делает запрос с просроченным access токеном, мы проверяем refresh, если он проходит проверку - выдаем новый access).
Единственное, придется куку на refresh ставить на все приложение (т. е. с каждым запросом будут уходить 2 токена, только зачем - пока непонятно), а не только на /auth, как и access (ну или делать отдельный запрос /auth/refresh-tokens).
3. Оба ли эти токены будут являться JWT?
  • Вопрос задан
  • 387 просмотров
Решения вопроса 1
nowm
@nowm
3. Оба ли эти токены будут являться JWT?

JWT — это строка, сформированная в соответствие с RFC7519. Если формат правильный, это JWT, если нет — это не JWT. Если вы распечатаете JWT-токен на бумаге и используете её как кулёк для семечек, эти токены не перестанут быть JWT-токенами. То есть, область применения никак не влияет на то, JWT это или нет — только формат самого токена.

---

Суть Access- и Refresh-токенов в том, что у вас может быть 10 разных серверов, один из которых используется для авторизации. На сервере авторизации вы генерируете токены и сообщаете их клиенту, каким-то способом, безопасность и удобство которого вас устраивает. Кто-то в куках передаёт, кто-то прямыми ответами сервера. Остальные 9 серверов принимают запросы, в которых указаны Access-токены. Тут тоже можно разными способами это делать. Можно в GET-параметрах токен передавать, можно в куках, можно в теле запроса, можно использовать хэдер Authorization — опять же, это на ваше усмотрение, в зависимости от того, какой способ вам кажется более удобным или более безопасным. На этих 9 серверах есть открытые ключи авторизационного сервера либо они могут обратиться к авторизационному серверу за ними. Используя эти открытые ключи, они проверяют Access-токен на валиднось, и если всё хорошо, выполняют запрос, в котором был этот токен, вытаскивая из токена, например, идентификатор текущего пользователя и используя его дальше. Сами эти 9 серверов не занимаются генерацией токенов вообще. Они только проверяют их. Соответственно, им не требуется хранить в БД или в кэше конкретные токены, чтобы удостоверяться в легитимности запроса, им нужен только открытый ключ авторизационного сервера, закрытой частью которого он подписывает эти токены. Это упрощает на их стороне логику, и они могут сосредоточиться на своих узких задачах.

Авторизационный сервер занимается только генерацией токенов. Именно он решает, что, например, Access-токен должен жить 3 минуты, а Refresh-токен — 30 дней. Он делает свои личные проверки любых данных, которые может достать (IP, UA, идентификатор сессии и так далее) — вы сами решаете и сами пишете логику того, какие проверки нужно делать и как. Если вы считаете, что в полнолуние нужно отказывать в генерации токена для браузера Firefox пользователям из Кении, именно так и программируйте систему, потому что за вас библиотека JWT автоматом такие проверки делать не будет — это не её назначение. Её назначение проверять и создавать JWT-токены из данных, которые вы ей даёте на входе. Среди этих данных могут быть идентификаторы пользователя, сессии и т.п., если вы сами захотели их туда засунуть.

Когда вы запрашиваете новый Access-токен, передавая в параметрах Refresh-токен (опять же, передавать его можно разными способами в зависимости от ваших вкусов и того, как вы запрограммировали сервер), авторизационный сервер проверяет его валидность и принимает решение, выдавать новый токен или нет. Вполне можно построить систему так, чтобы Refresh-токен не приходилось хранить в БД на стороне сервера авторизации, потому что все нужные данные можно поместить прямо в payload токена — он всё равно подписывается, так что если клиент подменит там идентификатор пользователя, токен не пройдёт валидацию.

Фишка с Access- и Refresh-токенами нужна для того, чтобы организовать распределённую работу нескольких серверов. Если вы генерируете токены на сервере, а потом на этом же сервере принимаете запросы, в которых используется Access-токен, вы делаете что-то неправильно, потому что в таких ситуациях городить подобную архитектуру не нужно, и передавать Refresh-токен вместе с Access-токеном API-серверу тоже не нужно, потому что генерация новых токенов — это не его забота.

Немного выше я писал, что токены со стороны клиента можно передавать по-разному в зависимости от предпочтений: в куках, в GET-параметрах, в теле запроса или в хэдере Authorization. Хорошая практика — это когда вы передаёте их в хэдере Authorization и больше никак. Делаете запрос к API-серверу? В хэдер помещается Access-токен. Делаете запрос на продление токена к авторизационному серверу? В хэдер помещается Refresh-токен. Всё просто и однотипно.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы