passport.js RESTful auth


155

Как обрабатывать аутентификацию (локальную и Facebook, например), используя passport.js, через RESTful API вместо веб-интерфейса?

Особые проблемы связаны с передачей данных из обратных вызовов в ответ RESTful (JSON) по сравнению с использованием типичного res.send ({data: req.data}), настройкой начальной конечной точки / конечной точки входа в систему, которая перенаправляет на Facebook (/ login не может быть доступ через AJAX, потому что это не ответ JSON - это перенаправление на Facebook с обратным вызовом).

Я нашел https://github.com/halrobertson/test-restify-passport-facebook , но у меня проблемы с пониманием.

Кроме того, как passport.js хранит учетные данные аутентификации? Сервер (или это сервис?) Поддерживается MongoDB, и я ожидаю, что там будут храниться учетные данные (логин и соленый хеш pw), но я не знаю, имеет ли passport.js такую ​​возможность.


Поскольку вы новичок в узел, начать легко и проверить пример приложения для passport-facebook. После того, как вы начнете работать, следующий шаг - начать понимать, как работает Passport и как он хранит учетные данные. Подключение к Restify ( см. Здесь обновленную версию упомянутой вами) будет одним из последних шагов (или вы можете реализовать интерфейс REST в Express).
Робертклеп

Ответы:


312

Здесь задается много вопросов, и кажется, что хотя вопросы задаются в контексте Node и passport.js, реальные вопросы больше касаются рабочего процесса, чем того, как это сделать с определенной технологией.

Давайте использовать пример настройки @Keith, немного измененный для дополнительной безопасности:

  • Веб-сервер https://example.comобслуживает одностраничное клиентское приложение Javascript
  • Веб-сервис RESTful at https://example.com/apiпредоставляет поддержку сервера для многофункционального клиентского приложения.
  • Сервер реализован в Node и passport.js.
  • Сервер имеет базу данных (любого рода) с таблицей «пользователи».
  • Имя пользователя / пароль и Facebook Connect предлагаются в качестве параметров аутентификации
  • Богатый клиент делает запросы REST в https://example.com/api
  • Могут быть другие клиенты (например, телефонные приложения), которые используют веб-службу, https://example.com/apiно не знают о веб-сервере в https://example.com.

Обратите внимание, что я использую безопасный HTTP. По моему мнению, это необходимо для любой службы, доступной в открытом доступе, поскольку конфиденциальная информация, такая как пароли и токены авторизации, передается между клиентом и сервером.

Имя пользователя / пароль аутентификация

Давайте посмотрим, как работает простая старая аутентификация.

  • Пользователь подключается к https://example.com
  • Сервер обслуживает богатое приложение Javascript, которое отображает начальную страницу. Где-то на странице есть форма авторизации.
  • Многие разделы этого одностраничного приложения не были заполнены данными из-за того, что пользователь не вошел в систему. Во всех этих разделах имеется прослушиватель событий для события «login». Все это на стороне клиента, сервер не знает об этих событиях.
  • Пользователь вводит свой логин и пароль и нажимает кнопку отправки, которая запускает обработчик Javascript для записи имени пользователя и пароля в переменных на стороне клиента. Затем этот обработчик запускает событие входа в систему. Опять же, это все действия на стороне клиента, учетные данные еще не отправлены на сервер .
  • Слушатели события "login" вызываются. Теперь каждому из них необходимо отправить один или несколько запросов в API RESTful at https://example.com/apiдля получения пользовательских данных, отображаемых на странице. Каждый отдельный запрос, отправляемый веб-службе, будет включать имя пользователя и пароль, возможно, в форме базовой аутентификации HTTP , поскольку службе, имеющей RESTful, не разрешено поддерживать состояние клиента от одного запроса к следующему. Поскольку веб-служба работает по безопасному HTTP, пароль безопасно шифруется во время транзита.
  • Веб-сервис https://example.com/apiполучает несколько отдельных запросов, каждый из которых содержит информацию для аутентификации. Имя пользователя и пароль в каждом запросе сверяются с базой данных пользователей, и если они найдены правильно, запрошенная функция выполняется и данные возвращаются клиенту в формате JSON. Если имя пользователя и пароль не совпадают, клиенту отправляется ошибка в виде кода ошибки 401 HTTP.
  • Вместо того, чтобы заставлять клиентов отправлять имя пользователя и пароль при каждом запросе, у вас может быть функция «get_access_token» в вашей службе RESTful, которая принимает имя пользователя и пароль и отвечает токеном, который является своего рода криптографическим хешем, который уникален и имеет некоторый срок действия. дата, связанная с этим. Эти токены хранятся в базе данных с каждым пользователем. Затем клиент отправляет токен доступа в последующих запросах. Токен доступа будет затем проверен по базе данных вместо имени пользователя и пароля.
  • Клиентские приложения, не являющиеся браузерами, такие как телефонные приложения, выполняют те же действия, что и выше, они просят пользователя ввести свои учетные данные, а затем отправлять их (или сгенерированный из них токен доступа) при каждом запросе к веб-службе.

В этом примере важно отметить, что веб-сервисы RESTful требуют аутентификации при каждом запросе .

Дополнительный уровень безопасности в этом сценарии добавил бы авторизацию клиентского приложения в дополнение к аутентификации пользователя. Например, если у вас есть веб-клиент, приложения для iOS и Android, все из которых используют веб-сервис, вы можете попросить сервер знать, какой из трех клиентов данного запроса, независимо от того, кто является аутентифицированным пользователем. Это может позволить вашему веб-сервису ограничить определенные функции определенными клиентами. Для этого вы можете использовать API ключи и секреты, см. Этот ответ для некоторых идей на этот счет.

Аутентификация в фейсбуке

Приведенный выше рабочий процесс не работает для подключения к Facebook, поскольку при входе через Facebook используется сторонняя организация, сама Facebook. Процедура входа требует, чтобы пользователь был перенаправлен на веб-сайт Facebook, где учетные данные вводятся вне нашего контроля.

Итак, давайте посмотрим, как все меняется:

  • Пользователь подключается к https://example.com
  • Сервер обслуживает богатое приложение Javascript, которое отображает начальную страницу. Где-то на странице есть форма входа, которая включает кнопку «Войти через Facebook».
  • Пользователь нажимает кнопку «Войти через Facebook», которая является просто ссылкой, на которую перенаправляется (например) https://example.com/auth/facebook.
  • https://example.com/auth/facebookМаршрут обрабатывается passport.js (см документации )
  • Все, что видит пользователь, - это то, что страница меняется, и теперь они находятся на странице, размещенной на Facebook, где им необходимо войти в систему и авторизовать наше веб-приложение. Это полностью вне нашего контроля.
  • Пользователь входит в систему Facebook и дает разрешение нашему приложению, поэтому теперь Facebook перенаправляет обратно на URL-адрес обратного вызова, который мы настроили в настройке passport.js, что соответствует примеру в документации :https://example.com/auth/facebook/callback
  • Обработчик passport.js для https://example.com/auth/facebook/callbackмаршрута вызовет функцию обратного вызова, которая получает токен доступа Facebook и некоторую информацию о пользователе от Facebook, включая адрес электронной почты пользователя.
  • С помощью электронной почты мы можем найти пользователя в нашей базе данных и сохранить с ним токен доступа Facebook.
  • Последнее, что вы делаете в обратном вызове Facebook, - это перенаправляете обратно в приложение расширенного клиента, но на этот раз нам нужно передать имя пользователя и токен доступа клиенту, чтобы он мог их использовать. Это можно сделать несколькими способами. Например, переменные Javascript могут быть добавлены на страницу через серверный механизм шаблонов, или же файл cookie может быть возвращен с этой информацией. (спасибо @RyanKimber за указание на проблемы безопасности при передаче этих данных в URL, как я изначально предлагал).
  • Итак, теперь мы запускаем одностраничное приложение еще раз, но у клиента есть имя пользователя и токен доступа.
  • Клиентское приложение может немедленно инициировать событие входа в систему и позволить различным частям приложения запрашивать необходимую информацию из веб-службы.
  • Все запросы отправлены https://example.com/api будут включать токен доступа Facebook для аутентификации или собственный токен доступа приложения, сгенерированный из токена Facebook через функцию «get_access_token» в REST API.
  • В не-браузерных приложениях это немного сложнее, потому что для входа в систему OAuth требуется веб-браузер. Чтобы войти в систему с телефона или из настольного приложения, вам потребуется запустить браузер, чтобы выполнить перенаправление на Facebook, и, что еще хуже, вы нужно, чтобы браузер передавал маркер доступа Facebook обратно в приложение через какой-то механизм.

Я надеюсь, что это отвечает на большинство вопросов. Конечно, вы можете заменить Facebook на Twitter, Google или любой другой сервис аутентификации на основе OAuth.

Мне было бы интересно узнать, есть ли у кого-то более простой способ справиться с этим.


5
Спасибо за ваш подробный ответ. Только один вопрос: ты так говоришь Every single request they send to the web service will include the username and password, и все же ты говоришь you can have a "get_access_token" function in your RESTful service. Кажется противоречивым утверждать, что REST должен быть без сохранения состояния, но хранение токенов доступа на стороне сервера - это нормально, поскольку этот акт хранения токенов доступа означает, что сервер теперь находится в состоянии. Буду признателен за любые разъяснения или обоснования по этому поводу. Спасибо! :)
Райанри

6
Когда я думаю о требовании отсутствия состояния, я думаю о состоянии определенного сеанса с клиентом. Я не вижу хранения данных, связанных с пользователем в базе данных, таких как пароль или маркер доступа, как «состояние», по крайней мере, не «состояние сеанса». Ваш сервер, очевидно, должен знать, кто такие пользователи и их пароли, но это данные приложения, которые не связаны с сеансом. Тем не менее, многие люди используют куки в предположительно RESTful-сервисах, поэтому насколько строго вы хотите придерживаться спецификации REST, зависит от каждого разработчика.
Мигель

1
@Dexter: В традиционном случае входа в систему пользователь вводит имя пользователя и пароль в форму, и когда он нажимает кнопку «Отправить», эта информация публикуется на веб-сервере. В этом случае этого не происходит, пользователь заполняет форму, и когда он нажимает Отправить, обработчик Javascript (событие onClick в кнопке отправки) захватывает данные и сохраняет их в контексте клиента. У меня нет готового примера для показа, но обратите внимание на вторую часть этого урока в моем блоге, где я покажу, как это делается: blog.miguelgrinberg.com/post/…
Мигель

2
Это очень хорошо продуманная статья, но она содержит одну важную оплошность или ошибку. При обработке входа в Facebook (или Github, Twitter и т. Д.) Было бы гораздо предпочтительнее передать токен клиенту в файле cookie, а затем удалить его на стороне клиента после его обнаружения. Передача токена как части строки URL-адреса добавит этот URL-адрес в историю браузера и (в случае неправильной обработки) может привести к тому, что этот URL-адрес будет запрошен браузером. Либо делает маркер видимым. Любой, кто скопировал URL-адрес, может подделать этого пользователя в вашем приложении.
Райан Кимбер

1
@ Натан: базовая аутентификация через https безопасна, так что да, это приемлемый механизм.
Мигель

11

Я высоко ценю объяснение @ Мигеля с полным потоком в каждом случае, но я хотел бы добавить некоторые в части аутентификации Facebook.

Facebook предоставляет Javascript SDK который можно использовать для получения токена доступа непосредственно на стороне клиента, который затем передается на сервер и используется для дальнейшего извлечения всей пользовательской информации из Facebook. Таким образом, вам не нужно никаких перенаправлений в принципе.

Кроме того, вы можете использовать ту же конечную точку API для мобильных приложений. Просто используйте Android / iOS SDK для Facebook, получите Facebook access_token на стороне клиента и передайте его на сервер.

Что касается природы без сохранения состояния, как объяснено, когда get_access_token используется для генерации токена и его передачи клиенту, этот токен также сохраняется на сервере. Так что это так же хорошо, как токен сеанса, и я верю, что это делает его состоянием?

Просто мои 2 цента ..


1
Это очень важно, потому что таким образом вы можете выполнять одностраничную аутентификацию ajax только с помощью Facebook. Токен РАВНО защищен как сеанс аутентификации, единственное отличие состоит в том, что токен может быть передан в другой домен, и сеанс используется только в одном конкретном домене. Используя expressjs и passport, вы можете создать API, который сохраняет состояние, и использовать аутентификацию сеанса.
Джерелли

Javascript api отлично подходит, если вы хотите аутентифицировать пользователя для выполнения действий против Facebook, но бесполезен сам по себе, если вы хотите проверить пользователя по вашему серверу / базе данных, насколько я могу судить.
Джеймс Вестгейт

4
Вы также можете использовать метод, описанный Мигелем выше, но затем выдать свой собственный токен JWT в качестве файла cookie при перенаправлении клиента в обратном вызове auth. Это обеспечивает лучшее из обоих миров - ваше одностраничное приложение может заботиться только об одном типе аутентификации (JWT), сохраняя при этом тот же уровень безопасности и предоставляя гибкость для поддержки любого из социальных учетных записей без необходимости использования специальные JavaScript API для каждой социальной сети (Facebook, Twitter, LinkedIn, Google и т. д.). Это также позволяет вам поддерживать AJAX-стиль поддержки имени пользователя / пароля и доступа к REST.
Райан Кимбер

Facebook Javascript SDK в настоящее время не работает с Chrome iOS. Может быть, проблема для некоторых.
demisx

@RyanKimber Можете ли вы написать маленькое git-репо или подобное, где это сделано в качестве примера, я полностью застрял
Саймон Драгсбек

3

Вот удивительная статья, которую я нашел, которая может помочь вам пройти аутентификацию:

  • facebook
  • щебет
  • Google
  • Местный Аут

Простая идентификация узла: настройка и локальная


Ваша ссылка ведет не к статье, а к списку статей с тегом «javascript»
Пауло Оливейра

1
Спасибо. Обновил ссылку. Они изменились, было это.
myusuf
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.