После формы регистрации осталось добавить вход в кабинет и восстановление пароля. Для этого перед программированием нам нужно определиться, как мы будем осуществлять аутентификацию в приложении из JS-фронтенда и API.
Как раз сегодня рассмотрим практики аутентификации по токенам при работе с API и сравним классические подходы с использованием токенов в формате JWT.
И на слайдах смоделируем по шагам процесс получения и обновления токенов по универсальной спецификации OAuth2, чтобы с нашим API могли работать сторонние клиенты:
И в следующих эпизодах реализуем весь процесс аутентификации на бэкенде и фронтенде.
Чтобы не пропускать новые эпизоды подпишитесь на наш канал @deworkerpro в Telegram
Спасибо))) Совсем скоро пойдёт домен! Ура)
Ну не так скоро конечно)
Да) Не думал, что так затянется. Дмитрий слишком требователен к контенту, поэтому много времени ушло на перезаписи старых
Спасибо!
Дмитрий, извините за оффтоп, можете, пожалуйста, порекомендовать какой пакет лучше использовать для имплементации OAuth с PKCE в Laravel, для SPA с раздельным API приложением? Спасибо за Ваш труд!
Для этого как раз есть Laravel Passport
Спасибо!
Спасибо. :)
Дмитрий, подскажите, пожалуйста, почему в Value Object Id для генерации ID мы используем фабричный метод
generate()
вместо того, чтобы вынести это в сервис IdentifierGenerator и в нем метод `generate()’.Тогда бы мы подключали этот генератор в командах и использовали бы примерно так:
Это бы позволило вынести генерацию Id в отдельный сервис. Отдельно тестировать его и не давать VO больше отвественности, чем ему нужно...
Да, можно при желании.
Имеет смысл переносить генерацию в репозиторий при использовании инкрементных идентификаторов, когда мы можем поместить туда доставание следующего номера из секвенции:
чтобы потом вызывать этот метод:
Выносить в
IdentifierGenerator
имеет смысл если мы пишем юнит-тесты к хэндлерам команд и там хотим подменять этот генератор, чтобы он возвращал фиксированное значение.Но как альтернативу этому можно генерировать значение прямо в контроллере и его передавать в команду в поле
$id
:и потом брать его в хэндлере из команды:
Тогда
$this->identifier->generate()
илиUuid::uuid4()->toString()
потребуется вызывать только в контроллере. Такой подход актуален если нам прямо из контроллера нужно будет сразу вернуть идентификатор. И в том случае, если команды у нас выполняются асинхронно.Да, я именно пришёл к тому, чтобы генерацию ID использовать даже не в командах, а контроллере. Иногда даже требуется генерировать ID на JS и передавать его в API. В любом случае спасибо за развёрнутый ответ)
А по поводу выноса в репозиторий - да. Видел у вас это. Однако, когда у нас есть отдельный сервис генерации ID мы можем реализацию метода generate() заменять как угодно. Если нужно - подключили в этот сервис репозиторий и запросили
nextId()
. Если не нужно сгенерируем IdUuid::uuid4()->toString()
С точки зрения безопасности иметь access token и refresh токен равносильно что иметь только один из них. Да, это удобно для перелогинивания пользователя через короткое время, чтобы обновить информацию в токене, например поменялась роль пользователя и когда истечет access токен вы используя refresh получите новый access токен и новые права доступа.
Но то что это делает процесс безопаснее - неправда, обычно рассматривают (как и в данном видео) ситуацию что хакер перехватил access token и у него только пару минут что-то там сломать и этого мало. На самом деле хакер перехватил и access и refresh токены, а далее используя refresh токен получает новый access токен раньше вас - в итоге у него полный доступ к вашему аккаунту, а вас разлогинело. И уже для решения таких атак нужно делать refresh token rotation и если дважды клиент обращается с одним и тем же refresh токеном то нужно инвалидировать все токены пользователя выпущенные после refresh токена
>> С точки зрения безопасности иметь access token и refresh токен равносильно что иметь только один из них.
Не всегда. По идее refresh токен нужно устанавливать в куки с флагом httpOnly, тогда js скрипт не сможет получить к нему доступ, а браузер сам сможет отправлять такой токен по маршруту который выдает новый access токен. При такой структуре получается что refresh token хранится в куках браузера, а не в приложении, поэтому он видится чуть более защищенным.
Интересное решение, спасибо
Где хранить 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?
В случае только браузерного рендеринга хранить в Local Storage. Если же помимо браузера рендеринг производится и на JS-сервере по Server Side Rendering (SSR), то можно хранить в Cookies, чтобы токен был доступен и в браузере, и на сервере.
Да. Токен станет невалидным, так как подпись уже не будет соответствовать данным.
Хранить IP-адрес не очень удобно, так как со смартфона будет постоянно разлогинивать при каждом выходе из дома при переходе с WiFi на LTE и обратно.
То делаем запрос получения нового access token по refresh token.
То очищаем его из своих Storage и при желании редиректим посетителя на страницу логина.
Такое маловероятно, так как refresh действует дольше access.
То считаем такой токен невалидным.
На каждом устройстве будут свои access и refresh токены.
Реализовали у себя в следующих эпизодах через очистку refresh-токенов по user_id.
Реализовали у себя очистку устаревших refresh-токенов и одноразовых кодов по Cron.
Спасибо Дмитрий за столь исчерпывающие ответы!
Дмитрий, а где можно скачать разные используемые вами материалы, например презентацию как в этом видео?
А для чего она может понадобиться отдельно от видео?
Или войти через: