Аутентификация OAuth2 и токены JWT

После формы регистрации осталось добавить вход в кабинет и восстановление пароля. Для этого перед программированием нам нужно определиться, как мы будем осуществлять аутентификацию в приложении из JS-фронтенда и API.

Как раз сегодня рассмотрим практики аутентификации по токенам при работе с 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

И в следующих эпизодах реализуем весь процесс аутентификации на бэкенде и фронтенде.

Скрытый контент
Комментарии (11)
Максим

Спасибо))) Совсем скоро пойдёт домен! Ура)

Ответить
Руслан

Спасибо!

Ответить
Руслан

Дмитрий, извините за оффтоп, можете, пожалуйста, порекомендовать какой пакет лучше использовать для имплементации OAuth с PKCE в Laravel, для SPA с раздельным API приложением? Спасибо за Ваш труд!

Ответить
Дмитрий Елисеев

Для этого как раз есть Laravel Passport

Ответить
Руслан

Спасибо!

Ответить
Arunas

Спасибо. :)

Ответить
Максим

Дмитрий, подскажите, пожалуйста, почему в Value Object Id для генерации ID мы используем фабричный метод generate() вместо того, чтобы вынести это в сервис IdentifierGenerator и в нем метод `generate()’.

Тогда бы мы подключали этот генератор в командах и использовали бы примерно так:

$id = new Id($this->identifier->generate());

Это бы позволило вынести генерацию Id в отдельный сервис. Отдельно тестировать его и не давать VO больше отвественности, чем ему нужно...

Ответить
Дмитрий Елисеев

Да, можно при желании.

Имеет смысл переносить генерацию в репозиторий при использовании инкрементных идентификаторов, когда мы можем поместить туда доставание следующего номера из секвенции:

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 {
    public string $id;
    public string $email;
    public string $password;
}

и потом брать его в хэндлере из команды:

$id = new Id($command->id);

Тогда $this->identifier->generate() или Uuid::uuid4()->toString() потребуется вызывать только в контроллере. Такой подход актуален если нам прямо из контроллера нужно будет сразу вернуть идентификатор. И в том случае, если команды у нас выполняются асинхронно.

Ответить
Максим

Да, я именно пришёл к тому, чтобы генерацию ID использовать даже не в командах, а контроллере. Иногда даже требуется генерировать ID на JS и передавать его в API. В любом случае спасибо за развёрнутый ответ)

А по поводу выноса в репозиторий - да. Видел у вас это. Однако, когда у нас есть отдельный сервис генерации ID мы можем реализацию метода generate() заменять как угодно. Если нужно - подключили в этот сервис репозиторий и запросили nextId(). Если не нужно сгенерируем Id Uuid::uuid4()->toString()

Ответить
Алескандр

С точки зрения безопасности иметь access token и refresh токен равносильно что иметь только один из них. Да, это удобно для перелогинивания пользователя через короткое время, чтобы обновить информацию в токене, например поменялась роль пользователя и когда истечет access токен вы используя refresh получите новый access токен и новые права доступа.

Но то что это делает процесс безопаснее - неправда, обычно рассматривают (как и в данном видео) ситуацию что хакер перехватил access token и у него только пару минут что-то там сломать и этого мало. На самом деле хакер перехватил и access и refresh токены, а далее используя refresh токен получает новый access токен раньше вас - в итоге у него полный доступ к вашему аккаунту, а вас разлогинело. И уже для решения таких атак нужно делать refresh token rotation и если дважды клиент обращается с одним и тем же refresh токеном то нужно инвалидировать все токены пользователя выпущенные после refresh токена

Ответить
alex

>> С точки зрения безопасности иметь access token и refresh токен равносильно что иметь только один из них.

Не всегда. По идее refresh токен нужно устанавливать в куки с флагом httpOnly, тогда js скрипт не сможет получить к нему доступ, а браузер сам сможет отправлять такой токен по маршруту который выдает новый access токен. При такой структуре получается что refresh token хранится в куках браузера, а не в приложении, поэтому он видится чуть более защищенным.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Google
GitHub
Yandex
MailRu