PassportJS - объяснение запутанных частей

PassportJS потрясающий. Он обеспечивает уровень абстракции при входе в систему с различными поставщиками, такими как Facebook, Google, Github, Twitter и другими.

Однако при первом запуске может быть немного сложно понять, что происходит и почему. Их документация довольно хороша, но не учитывает некоторые особенности, которые мне изначально было трудно понять. Надеюсь, это поможет ответить на некоторые из ваших вопросов и устранит некоторую путаницу. Я предполагаю, что вы немного прочитали документацию и, возможно, пытались ее реализовать. Даже если вы этого не сделали, вы все равно сможете продолжить. Даже если да, это все равно может оказаться полезным для лучшего понимания PassportJS.

Вот что покрыто

  • Функция обратного вызова в настройке стратегии
  • Почему passport.authenticate() нужен в обратном вызове
  • Проверка входа пользователя в систему
  • Понимание serializeUser() и deserializeUser()

Хорошо, пошли!

Проблема 1. Описание обратного вызова при настройке стратегии

Взгляните на приведенный ниже код. Нам требуются наши модули, мы настраиваем нашу стратегию Google и создаем два маршрута, один для обработки входа в систему, а другой для callbackURL.

Когда пользователь посещает /login/google, мы аутентифицируемся в Google и в этом случае запрашиваем их email и profile информацию.

Однако одна из ключевых составляющих - как часть passport.authenticate(), функция в строке 9 вызывается до того, как console.log() находится в строке 15.

Эта функция - первый раз, когда у вас есть доступ к информации профиля. Обычно именно здесь вы добавляете пользователя в свою базу данных, если она еще не содержит его (см. Пример кода ниже). Но как выйти из этой функции, чтобы вернуться к своему коду, в частности к обработчику маршрута для callbackURL, указанного в строке 19.

Для этого вы звоните done().

Что такое done()? Внутренняя функция PassportJS. Вы передаете ему два аргумента, null и информацию о профиле следующим образом:

Он берет информацию профиля и прикрепляет ее к объекту запроса, чтобы она была доступна на вашем URL-адресе обратного вызова как req.user. Это будет доступно для маршрута /login/google/return, как определено в строке 41, и именно здесь вы должны установить сеанс для пользователя, а затем отправить его по пути в другую часть вашего сайта.

Проблема 2: почему в callbackURL указан паспортный код .authenticate ()?

Если пользователь возвращается к /login/google/return, зачем нам еще passport.authenticate()? Частично это сделано для того, чтобы пользователь не просто перешел на маршрут напрямую и не подтвердил, что он действительно вошел в систему. Это обрабатывается внутри PassportJS.

В качестве побочного примечания вы не должны использовать passport.authenticate() как метод проверки того, вошел ли пользователь в систему. Это следует использовать только для фактического входа пользователя в систему, которая необходима в этом случае, поскольку они могут еще не войти в систему, если они просто набрали в URL-адресе и маршруте и нажмите Enter.

Проблема 3. Проверьте, вошел ли пользователь в систему

В строке 36 в блоке кода выше мы устанавливаем req.session.user равным профилю человека, который только что вошел в систему. Чтобы проверить, вошел ли кто-то в систему, мы можем просто посмотреть, установлено ли это значение, а затем использовать эту функцию в качестве промежуточного программного обеспечения на любом маршруте. мы хотим, где пользователь должен войти в систему. Вот пример функции промежуточного программного обеспечения.

Затем, если вы хотите убедиться, что пользователь вошел в систему для маршрута, просто сделайте это

Это вызовет isLoggedIn. Если установлено req.session.user, запрос продолжается из-за вызова next(). В противном случае пользователь будет перенаправлен на /login.

Вопрос 4. Что это за бизнес по сериализации и десериализации?

Выше мы сказали, что паспорт прикрепляет данные профиля к req.user. Это происходит как прямой результат работы функций serializeUser() и deserializeUser() .

Когда вы пишете done() в функции обратного вызова, указанной при настройке стратегии (посмотрите на второй блок кода полностью вверху, где мы делаем done(null, dbUserRecord или done(null, newUser)), мы передаем весь объект профиля пользователя (в этом примере либо dbUserRecord или как newUser).

Причина в том, что оно получено serializeUser(). Затем эта функция вызывает done(null, user.id). Passport принимает этот идентификатор пользователя и сохраняет его внутри req.session.passport, который является внутренним механизмом паспорта для отслеживания вещей.

Затем в deserializeUser() первым аргументом является id, который совпадает с id, который был передан в done(null, user.id) из serializeUser(). deserializeUser() затем отправляет запрос в нашу базу данных, чтобы найти полную информацию о профиле пользователя, а затем вызывает done(null, user). Здесь профиль пользователя прикрепляется к обработчику запросов по адресу req.user. Затем, наконец, после того, как все это произойдет, пользователь возвращается к обработчику маршрута /login/google/return, где мы наконец можем получить доступ к информации профиля пользователя на req.user.

Чтобы лучше понять этот пользовательский поток, взгляните на диаграмму в этом ответе StackOverflow: https://stackoverflow.com/questions/27637609/understanding-passport-serialize-deserialize?answertab=votes#tab-top.

Подведение итогов

Вы можете спросить себя, зачем все это нужно. Обработка информации о пользователе таким образом означает, что PassportJS должен хранить только идентификатор пользователя, а не весь профиль пользователя. Это снижает вероятность запутать req.session.user, который мы установили, и req.session.passport.user, который является способом PassportJS отслеживать, что происходит.

Это можно увидеть, зарегистрировав req.session, который вернет что-то вроде этого

Обратите внимание, что passport.user совпадает с user._id. Они должны быть такими, чтобы мы могли выполнять поиск в базе данных с нужной информацией. Кроме того, passport.user сохраняет только идентификатор и ничего, кроме name или googleID, которые мы видим в объекте user.

Заключение

PassportJS действительно упрощает настройку, но понимание потока данных может сбивать с толку. Я надеюсь, что это поможет объяснить некоторые из основных принципов, которые он использует. Если у вас есть вопросы, оставьте их в комментариях.

Спасибо за прочтение!