Для начала, вам необходимо будет указать данные из личного кабинета: email, password и appId.
Сделав запрос, в ответ вы получите токены: accessToken и refreshToken:
- accessToken нужен для выполнения запросов к API и действует 24 часа
- accessTokenEndTime — это время (unix timestamp) окончания действия accessToken.
- refreshToken необходим для получения новой пары токенов (продления доступа) и действует 14 дней.
- refreshTokenEndTime — это время (unix timestamp) окончания действия refreshToken.
Полученные токены необходимо сохранить на стороне приложения для дальнейшей возможности выполнять запросы к API и продления доступа.
Продление доступа
Чтобы продлить доступ и получить новую пару токенов, используйте метод tokens.refresh.
Полученные при такой процедуре токены, также необходимо сохранить на стороне приложения , чтобы иметь возможность повторно выполнять запросы к API и продлевать доступ.
Рекомендации по продлению токена доступа
Аутентификация OAuth2 и токены JWT
После формы регистрации осталось добавить вход в кабинет и восстановление пароля. Для этого перед программированием нам нужно определиться, как мы будем осуществлять аутентификацию в приложении из JS-фронтенда и API.
What are Refresh Tokens?! 🆔⌛
Как раз сегодня рассмотрим практики аутентификации по токенам при работе с API и сравним классические подходы с использованием токенов в формате JWT.
И на слайдах смоделируем по шагам процесс получения и обновления токенов по универсальной спецификации OAuth2, чтобы с нашим API могли работать сторонние клиенты:
- 00:02:39 — Basic Authentication
- 00:04:40 — Использование токенов
- 00:06:59 — Токены в распределённых системах
- 00:08:28 — Хранение данных в токене
- 00:11:06 — Инвалидация токена
- 00:12:34 — Механизм обновления
- 00:15:07 — JSON Web Token
- 00:18:22 — Пакет lcobucci/jwt
- 00:19:30 — Процесс аутентификации
- 00:20:03 — Вход через сторонние сервисы
- 00:24:07 — Использование одноразового кода
- 00:25:59 — Спецификация OAuth 2
- 00:28:04 — Доступ к разделам
- 00:30:36 — Модерация клиентов
- 00:35:33 — Refresh Token Grant
- 00:36:25 — Дополнение PKCE
И в следующих эпизодах реализуем весь процесс аутентификации на бэкенде и фронтенде.
Скрытый контент (код, слайды, . ) для подписчиков. Открыть →
Дмитрий Елисеев
Комментарии ( 22 )
2021-04-23 12:54
Спасибо))) Совсем скоро пойдёт домен! Ура)
Konstantin
2023-05-12 16:35
Ну не так скоро конечно)
2023-05-12 18:47
Да) Не думал, что так затянется. Дмитрий слишком требователен к контенту, поэтому много времени ушло на перезаписи старых
2021-04-23 14:10
2021-04-23 15:08
Дмитрий, извините за оффтоп, можете, пожалуйста, порекомендовать какой пакет лучше использовать для имплементации OAuth с PKCE в Laravel, для SPA с раздельным API приложением? Спасибо за Ваш труд!
Дмитрий Елисеев
2021-04-25 09:12
Для этого как раз есть Laravel Passport
2021-04-25 11:08
2021-04-23 17:14
2021-04-24 10:47
Дмитрий, подскажите, пожалуйста, почему в Value Object Id для генерации ID мы используем фабричный метод generate() вместо того, чтобы вынести это в сервис IdentifierGenerator и в нем метод `generate()’.
Тогда бы мы подключали этот генератор в командах и использовали бы примерно так:
$id = new Id($this->identifier->generate());
Это бы позволило вынести генерацию Id в отдельный сервис. Отдельно тестировать его и не давать VO больше отвественности, чем ему нужно.
Дмитрий Елисеев
2021-04-25 09:29
Да, можно при желании.
Имеет смысл переносить генерацию в репозиторий при использовании инкрементных идентификаторов, когда мы можем поместить туда доставание следующего номера из секвенции:
class UserRepository < . public function nextId(): Id < return new Id( (int)$this->connection->query(‘SELECT nextval(‘users_seq’)’)->fetchColumn() ); > >
чтобы потом вызывать этот метод:
$id = $this->users->nextId();
Выносить в IdentifierGenerator имеет смысл если мы пишем юнит-тесты к хэндлерам команд и там хотим подменять этот генератор, чтобы он возвращал фиксированное значение.
Но как альтернативу этому можно генерировать значение прямо в контроллере и его передавать в команду в поле $id :
class Command
и потом брать его в хэндлере из команды:
$id = new Id($command->id);
Тогда $this->identifier->generate() или Uuid::uuid4()->toString() потребуется вызывать только в контроллере. Такой подход актуален если нам прямо из контроллера нужно будет сразу вернуть идентификатор. И в том случае, если команды у нас выполняются асинхронно.
2021-04-25 09:42
Да, я именно пришёл к тому, чтобы генерацию ID использовать даже не в командах, а контроллере. Иногда даже требуется генерировать ID на JS и передавать его в API. В любом случае спасибо за развёрнутый ответ)
А по поводу выноса в репозиторий — да. Видел у вас это. Однако, когда у нас есть отдельный сервис генерации ID мы можем реализацию метода generate() заменять как угодно. Если нужно — подключили в этот сервис репозиторий и запросили nextId() . Если не нужно сгенерируем Id Uuid::uuid4()->toString()
2021-07-30 12:44
С точки зрения безопасности иметь access token и refresh токен равносильно что иметь только один из них. Да, это удобно для перелогинивания пользователя через короткое время, чтобы обновить информацию в токене, например поменялась роль пользователя и когда истечет access токен вы используя refresh получите новый access токен и новые права доступа.
Но то что это делает процесс безопаснее — неправда, обычно рассматривают (как и в данном видео) ситуацию что хакер перехватил access token и у него только пару минут что-то там сломать и этого мало. На самом деле хакер перехватил и access и refresh токены, а далее используя refresh токен получает новый access токен раньше вас — в итоге у него полный доступ к вашему аккаунту, а вас разлогинело. И уже для решения таких атак нужно делать refresh token rotation и если дважды клиент обращается с одним и тем же refresh токеном то нужно инвалидировать все токены пользователя выпущенные после refresh токена
2021-08-15 04:58
>> С точки зрения безопасности иметь access token и refresh токен равносильно что иметь только один из них.
Не всегда. По идее refresh токен нужно устанавливать в куки с флагом httpOnly, тогда js скрипт не сможет получить к нему доступ, а браузер сам сможет отправлять такой токен по маршруту который выдает новый access токен. При такой структуре получается что refresh token хранится в куках браузера, а не в приложении, поэтому он видится чуть более защищенным.
2021-10-10 00:06
Интересное решение, спасибо
2022-01-15 23:51
Где хранить access token в Local Storage, Session Storage, в локальной переменной внутри замыкания? Session Storage, внутри замыкания доступны только в одной вкладке. Остаётся Local Storage тк доступен между табами. Правильное ли это утверждение?
При попытке подменить данные в header-ре или payload-е, токен станет не валидным, поскольку сигнатура не будет соответствовать изначальным значениям. Так как секретный ключ для зашифровки лежит на сервере. Правильное ли это утверждение?
В access token не рекомендуется хранить какую либо sensitive data так как она не зашифрованная . Ок храним user_id, user_role в localStorage время жизни например 10 мин и отправляем в headere Authorization: Bearer token серверу
В refresh token храним в payloade user agent, fingerprint, ip, expiresIn в зашифрованном виде? Записываем в БД Записываем в Cookie (чревато атакой CSRF) решение иметь флаг Secure, httpOnly, Атрибут SameSite должен быть Strict + настроено Content-Security-Policy, X-Frame-Options, X-XSS-Protection, X-Content-Type-Options поможет ли Helmet? Нужен CSRF токен с учетом Fingerprint-a, maxAge куки ставим равную expiresIn, В path ставим корневой роут auth контроллера, время жизни например 30 дней
Зачем ставить куку с такими опциями /api/auth? чтобы запросы к публичной статике не содержали оверхед в header?
- Если access token истёк?
- Если refresh token истёк?
- Если refresh token истёк а access token нет?
- Если user agent другой
- Если fingerprint другой
- Если ip другой
- Что если пользователь зашёл с двух устройств?
- Реализовывать «Выйти на всех устройствах» в профиле юзера?
- Что делать в бд с устаревшими токенами?
Источник: deworker.pro
LocalStorage vs Cookies: все, что нужно знать о безопасном хранении токенов JWT во Front-End
В статье даются рекомендации по хранению и использованию JWT токенов. Если кратко резюмировать статью, то в ней рекомендуется полученные от бекенда access токены хранить в приложение, а refresh токены в куках. А вот если хотите узнать почему именно такие рекомендации читайте статью.
Токены JWT замечательны, но как их надежно хранить на фронтенде? Мы рассмотрим плюсы и минусы LocalStorage и cookie для хранения JWT.
Краткий обзор Access token и Refresh token
Токены доступа (Access token) обычно являются недолговечными токенами JWT, подписанными сервером и включенными в каждый HTTP-запрос к серверу для авторизации запроса.
Токены обновления (Refresh token) обычно представляют собой долговечные зашифрованные токенами JWT, хранящиеся в базе данных, которые используются для получения нового токена доступа по истечении срока его действия.
Где лучше хранить свои токены?
Существует два распространенных способа хранения токенов: в localStorage и в cookie. Есть много споров о том, какой из них лучше, но большинство людей склоняются к cookie из-за большей безопасности.
Давайте рассмотрим это сравнение чуть более подробнее.
Local Storage
Плюсы: Это удобно.
- Это чистый JavaScript и с ним удобней работать. Если у вас нет бэкэнда и вы полагаетесь на стороннее API, вы не всегда сможете установить определенные cookie для вашего приложения.
- Работает с API-интерфейсами, которые требуют, чтобы вы поместили свой токен доступа в заголовок запроса, типа такого Authorization Bearer $ .
Минусы: Такой способ уязвим к XSS атакам.
Атака XSS происходит, когда злоумышленник может запустить свой скрипт JavaScript на вашем сайте во время посещения его другим пользователем. Это означает, что злоумышленник сможет получит доступ к токену доступа, который вы сохранили в localStorage.
Cookies
Плюсы: Файл cookie может быть не доступен через JavaScript, поэтому он не так уязвим для атак XSS, как localStorage.
- Если вы используете флаги httpOnly, то cookie будут не доступны из JavaScript. Это означает, что даже если злоумышленник сможет запустить JS на сайте, он не сможет прочитать ваш токен доступа.
- Cookie автоматически отправляется в каждом HTTP-запросе на ваш сервер.
Минусы: В зависимости от варианта использования иногда вы не сможете хранить свои токены в файлах cookie..
- Размер файлов cookie ограничен 4 КБ, поэтому, если вы используете большие токены JWT, сохранение в файле cookie станет не возможным.
- Существуют сценарии, когда вы не можете использовать cookie напрямую для доступа к серверному API. Например когда требуется наличие токена в заголовке запроса.
Кратко о XSS атаках
Итак мы уже сказали что local storage уязвим, так как он легко доступен с помощью JavaScript, и злоумышленник может получить access token и использовать его. Однако, хотя cookie с httpOnly недоступны из JavaScript, это не означает, что с помощью cookie вы полностью защищены от XSS атак.
Если злоумышленник может запустить JavaScript в вашем приложении, он все равно сможет отправить HTTP-запрос на ваш сервер получив таким образом все токены. Но для злоумышленника это менее удобно, потому что он не сможет прочитать содержимое токена, хотя это и не всегда нужно.
Cookies и CSRF Атаки
CSRF Attack – это атака, которая позволяет сделать запрос от имени пользователя но без его ведома. Например, если веб-сайт принимает запрос на изменение электронной почты через такой запрос:
POST /email/change HTTP/1.1 Host: site.com Content-Type: application/x-www-form-urlencoded Content-Length: 50 Cookie: session=abcdefghijklmnopqrstu email=myemail.example.com
То злоумышленник может легко создать форму на вредоносном веб-сайте, которая отправит POST запрос на https://site.com/email/change со скрытым полем электронной почты, и cookie с токенами будут автоматически включены в этот запрос.
Однако это можно легко исправить, используя флаг cookie sameSite и добавив anti-CSRF token.
Вывод
Хотя куки все еще имеют некоторые уязвимости, они предпочтительнее по сравнению с localStorage, когда их применение возможно. И вот почему?
- Как localStorage, так и cookie уязвимы для атак XSS, но злоумышленнику сложнее выполнить эту атаку, когда вы используете cookie с httpOnly.
- Cookie уязвимы для атак CSRF, но их вероятность можно уменьшить с помощью флага sameSite и токенов защиты от CSRF (anti-CSRF tokens).
- Вы все еще можете использовать cookie, даже если вам нужно использовать заголовок Authorization: Bearer или ваш JWT больше 4 КБ. Это также согласуется с рекомендацией сообщества OWASP:
Итак, как мне использовать куки для сохранения моих токенов OAuth 2.0?
Напомним, вот несколько способов хранения ваших токенов:
- Вариант 1: Хранение Access Token в localStorage: но это выбор уязвим к XSS атакам.
- Вариант 2: Хранение Access Token в cookie с httpOnly: но этот выбор будет уязвим к CSRF атакам, хотя его можно уменьшить, что лучше чем XSS.
- Вариант 3: Хранение Refresh Token в cookie c httpOnly: это будет безопасно от CSRF атак, и немного лучше от XSS.