Passport.js RESTful auth

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

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

Я нашел https://github.com/halrobertson/test-restify-passport-facebook, но мне трудно понять его.

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

Ответ 1

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

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

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

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

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

Аутентификация Facebook

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

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

  • Пользователь подключается к https://example.com
  • Сервер обслуживает богатое приложение Javascript, которое отображает начальную страницу. Кто-то на странице есть форма входа, в которую входит кнопка "Войти с Facebook".
  • Пользователь нажимает кнопку "Войти с Facebook", которая является только ссылкой, которая перенаправляется (например) https://example.com/auth/facebook.
  • Маршрут https://example.com/auth/facebook обрабатывается паспортом (см. документация)
  • Все пользователи видят, что страница меняется, и теперь они находятся на странице, размещенной на Facebook, где им необходимо войти в систему и авторизировать наше веб-приложение. Это полностью вне нашего контроля.
  • Пользователь регистрируется в Facebook и предоставляет разрешение на наше приложение, поэтому Facebook теперь перенаправляет обратно на URL-адрес обратного вызова, который мы настроили в настройке passport.js, которая, следуя примеру документация https://example.com/auth/facebook/callback
  • Обработчик pass.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.

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

Ответ 2

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

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

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

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

Только мои 2 цента.