Что такое refresh token
Token, refresh token и создание асинхронной обертки для REST-запроса
В данном туториале мы кратко разберем, как реализовываются REST-запросы к API, требующие, чтобы пользователь был авторизован, и создадим асинхронную «обертку» для запроса, которая будет проверять авторизацию и своевременно ее обновлять.
Данные для авторизации
Сделав REST-запрос к api, куда мы отправили логин и пароль, в ответ мы получаем json следующего формата (значения взяты рандомные и строки обычно длиннее):
Полей в ответе может быть больше, например еще «token_type», «expires_on» и т. д., но, для данной реализации, нам нужны только три поля, приведенные выше.
Давайте их рассмотрим подробнее:
Получение токена
Теперь создадим функцию, которая будет получать json, описанный выше, и сохранять его.
Хранить данные для авторизации мы будем в sessionStorage или localStorage, в зависимости от наших нужд. В первом случае данные хранятся до тех пор, пока пользователь не завершит сеанс или не закроет браузер, во втором случае данные в браузере будут храниться неограниченное время, пока по каким-либо причинам localStorage не будет очищен.
Функция для сохранения токена в sessionStorage:
Функция для получения токена:
Таким образом мы получили токен с полями «access_token», «refresh_token» и «expires_in» и сохранили его в sessionStorage для дальнейшего использования.
Обновление токена
Токен полученный нами ранее имеет ограниченное время жизни, которое задано в поле «expires_in». После того как его время жизни истечет, пользователь не сможет получить новые данные, отправляя данный токен в запросе, поэтому нужно получить новый токен.
Получить токен мы можем двумя способами: первый способ это заново авторизовавшись, отправив логин и пароль на сервер. Но это нам не подходит, т. к. заставлять пользователя каждый раз заново вводить данные авторизации по истечению какого-то отрезка времени — неправильно, это надо делать автоматически. Но хранить где-то в памяти пару логин/пароль для автоматической отправки небезопасно, именно для этого и нужен «refresh_token», который был получен ранее вместе с «access_token» и хранится в sessionStorage. Отправив данный токен на другой адрес, который предоставляет api, мы сможем получить в ответ новый «свежий» токен.
Функция для обновления токена
С помощью кода выше мы перезаписали токен в sessionStorage и теперь по новой можем отправлять запросы к api.
Создание функции-обертки
Теперь создадим функцию, которая будет добавлять данные для авторизации в шапку запроса, и при необходимости их автоматически обновлять перед совершением запроса.
Так как в случае, если срок жизни токена истек, нам надо будет делать запрос нового токена, то наша функция будет асинхронной. Для этого мы будем использовать конструкцию async/await.
Функция-обертка
С помощью кода выше мы создали функцию, которая будет добавлять токен к запросам в api. На эту функцию мы можем заменить fetch в нужных нам запросах, где требуется авторизация и для этого нам не потребуется менять синтаксис или добавлять в аргументы еще какие-либо данные.
Просто достаточно будет «импортнуть» ее в файл и заменить на нее стандартный fetch.
Добавляем Refresh Token
Повышаем безопасность
Предлагаю читателям перед тем, как двигаться дальше самостоятельно, подумать, что мы можем сделать с безопасностью.
Приведу несколько подходов, которые могут улучшить безопасность. Можете писать в комментариях, какие еще существуют подходы — я их добавлю.
Вспомним, что такое JWT
JWT использует преимущества подхода цифровой подписи JWS (Signature) и кодирования JWE (Encrypting). Подпись не дает кому-то подделать токен без информации о секретном ключе, а кодирование защищает от прочтения данных третьими лицами.
Давайте разберемся, как они могут нам помочь для аутентификации и авторизации пользователя на сайте.
Аутентифика́ция (англ. authentication; от греч. αὐθεντικός [authentikos] – реальный, подлинный; от αὐθέντης [authentes] – автор) — процедура проверки подлинности. В нашем случае, мы проверяем логин + пароль на совпадение с записью в базе даных пользователей.
Авториза́ция (англ. authorization — разрешение, уполномочивание) — предоставление пользователю прав на выполнение определённых действий; а также процесс проверки (подтверждения) данных прав при попытке выполнения этих действий.
Другими словами, аутентификация проверяет легальность пользователя, а затем, если все хорошо, пользователь становится авторизированным, то есть может выполнять разрешенные действия с базой данных. Обычно, эти два процесса совмещаются, поэтому и возникает известная путанница.
Виды токенов
Ключевая идея разделения токенов состоит в том, что, с одной стороны, токены авторизации позволяют нам легко проверять пользователя без участия сервера авторизации, просто сравнением подписей.
В заключение
Зачем нужен Refresh Token, если есть Access Token?
Зачем вообще нужны токены
Зачем нужен первый токен
Есть много разных токенов. Обычные, криптографические, «access key», «session token», разные схемы получения, использования и revoke. При этом одна из ключевых идей заключается в том, что если кто нехороший получит чужой токен, то самое неприятное, что случится — это доступ похитителя к сервису, от которого токен похищен. Пароль, тот самый, который один на все сервисы, похититель не получит. А пользователь, если поймет, что кроме него к сервису получил доступ кто-то другой, может токен отозвать. После чего получить себе новый, имея логин и пароль.
Зачем нужен второй токен
В OAuth 2 и некоторых других схемах авторизации (например, у нас) есть не один, а целых два токена. Первый, access token, используется при запросах к серверу (например, при логине). У него есть два свойства: он многоразовый и короткоживущий. Наш, к примеру, живет 48 часов, а у кого-то 30 минут. Время выбирается на основании того, как используется сервис. Второй, refresh token, используется для обновления пары access и refresh токенов. У него тоже есть два свойства, обратные первому токену: он одноразовый и долгоживущий. Совсем долгоживуший, наш живет месяц.
Зачем на самом деле нужен второй токен
Все оказалось и проще, и сложнее чем я думал. Следите за руками:
Случай 1: Боб узнал оба токена Алисы и не воспользовался refresh
В этом случае Боб получит доступ к сервису на время жизни access token. Как только оно истечет и приложение, которым пользуется Алиса, воспользуется refresh token, сервер вернет новую пару токенов, а те, что узнал Боб, превратятся в тыкву.
Случай 2: Боб узнал оба токена Алисы и воспользовался refresh
В этом случае оба токена Алисы превращаются в тыкву, приложение предлагает ей авторизоваться логином и паролем, сервер возвращает новую пару токенов, а те, что узнал Боб, снова превратятся в тыкву (тут есть нюанс с device id, может вернуть ту же пару что и у Боба. В таком случае следующее использование refresh токена превратит токены Боба в то, что изображено справа).
Таким образом, схема refresh + access токен ограничивает время, на которое атакующий может получить доступ к сервису. По сравнению с одним токеном, которым злоумышленник может пользоваться неделями и никто об этом не узнает.
Так зачем же все таки нужны Refresh токены в OAuth?
Наверняка каждый программист, работающий с OAuth 2.0, задумывался – зачем же нужны Refresh токены, неужели Access токенов недостаточно? 64 KB — Их должно хватать каждому!
Эта тема довольно активно дискутируется – вот и на Stackoverflow вопрос есть и на Хабре тоже обсуждается. Собственно, именно обсуждение на Хабре и заставило меня высказаться.
Все предложенные комментаторами и авторами мнения касаются безопасности двухтокенного подхода. Безусловно, так и должно быть, ведь безопасность – это главное для фреймворка авторизации/аутентификации! Но будем откровенны – во многих случаях использования подход с двумя токенами не дает никакого выигрыша в защищенности по сравнению с простым и тупым подходом с одним токеном. Или этого сразу не видно…
«Refresh токен можно хранить более защищенно!» — можно и нужно, хотя почти никто так не делает.
«Access token передается по сети чаще – и вероятность его утечки больше» — полноте, мы ведь всегда используем TLS, правда?
«Утечка Ассеss токена на так страшна как утечка Refresh токена» — да, и это тоже правда, именно поэтому в браузер Refresh токен и не выдается…
Есть много нюансов, есть много сценариев использования, при которых использование разных токенов становится полезным, просто видно их не сразу!
Но есть и еще один аргумент, который я почему-то ни разу не встречал – хотя он, на мой взгляд, полностью объясняет, зачем же нужен Refresh токен и почему нельзя, абсолютно, категорически нельзя обойтись только Access токеном.
Производительность.
Причем речь идет не о сайте или приложении с миллионом–десятком миллионов пользователей, нет!
Давайте подумаем, как же должны работать с OAuth аутентификацией сервисы уровня Гугла, Фейсбука и Твиттера.
Вот получил бекенд этого Большого Сервиса Access токен. Как проверить его валидность?
Ну, можно поискать его в базе данных. Так как речь идет о Сервисе с Большой Буквы – то и База Данных будет большая, мощная, с Большой Буквы.
Да, похоже у нас с этой Базой будет проблема, правда? Давайте разместим ее в inMemory, давайте добавим несколько реплицируемых копий… Справимся? Наверное…
Или знаете что? Давайте будем хранить всю информацию о токене в нем самом! Так и называется – self-contained токен. Мы его конечно, при создании зашифруем и подпишем – а при получении расшифруем и проверим подпись. И извлечем все что надо: имя пользователя, его права и срок действия токена.
Да, такой подход, кажется и в самом деле будет работать! Не нужно чудовищно быстрой и ответственной Базы Данных Токенов! Любой наш сервер (а их у нас много, мы ведь распределенные!) легко и просто проверяет валидность токена и извлекает из него необходимую информацию о правах и доступах.
Так… а что мы будем делать если пользователь изменил пароль? Надо ведь все старые токены инвалидировать? Что будем делать, если администратор заблокировал пользователя или изменил его права?
Да, а ведь без базы данных-то и не обойтись! Ну так и давайте проверять права пользователя не всегда – а иногда, с некоторыми разумными интервалами. Ну скажем в момент обновления токенов – то есть раз в час!
Да, так оно и работает. Программисты, имплементирующие OAuth на стороне сервера аутентификации имеют выбор – они могут проверять права владельца токена каждый раз при получении Access токена – а могут делать это только изредка, при создании новой пары токенов.
Преимущества и недостатки обоих подходов очевидны – ну а Большие Сервисы и выбора-то не имеют – и работают по схеме self-contained токенов.
Вот, оказывается, и еще одно достоинство этого сложного, но такого гибкого фреймворка – oAuth 2.0!
Зачем нужен Refresh Token, если есть Access Token?
Зачем вообще нужны токены
Зачем нужен первый токен
Есть много разных токенов. Обычные, криптографические, «access key», «session token», разные схемы получения, использования и revoke. При этом одна из ключевых идей заключается в том, что если кто нехороший получит чужой токен, то самое неприятное, что случится — это доступ похитителя к сервису, от которого токен похищен. Пароль, тот самый, который один на все сервисы, похититель не получит. А пользователь, если поймет, что кроме него к сервису получил доступ кто-то другой, может токен отозвать. После чего получить себе новый, имея логин и пароль.
Зачем нужен второй токен
В OAuth 2 и некоторых других схемах авторизации (например, у нас) есть не один, а целых два токена. Первый, access token, используется при запросах к серверу (например, при логине). У него есть два свойства: он многоразовый и короткоживущий. Наш, к примеру, живет 48 часов, а у кого-то 30 минут. Время выбирается на основании того, как используется сервис. Второй, refresh token, используется для обновления пары access и refresh токенов. У него тоже есть два свойства, обратные первому токену: он одноразовый и долгоживущий. Совсем долгоживуший, наш живет месяц.
Зачем на самом деле нужен второй токен
Все оказалось и проще, и сложнее чем я думал. Следите за руками:
Случай 1: Боб узнал оба токена Алисы и не воспользовался refresh
В этом случае Боб получит доступ к сервису на время жизни access token. Как только оно истечет и приложение, которым пользуется Алиса, воспользуется refresh token, сервер вернет новую пару токенов, а те, что узнал Боб, превратятся в тыкву.
Случай 2: Боб узнал оба токена Алисы и воспользовался refresh
В этом случае оба токена Алисы превращаются в тыкву, приложение предлагает ей авторизоваться логином и паролем, сервер возвращает новую пару токенов, а те, что узнал Боб, снова превратятся в тыкву (тут есть нюанс с device id, может вернуть ту же пару что и у Боба. В таком случае следующее использование refresh токена превратит токены Боба в то, что изображено справа).
Таким образом, схема refresh + access токен ограничивает время, на которое атакующий может получить доступ к сервису. По сравнению с одним токеном, которым злоумышленник может пользоваться неделями и никто об этом не узнает.