Ответ 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.
Мне было бы интересно узнать, есть ли у кого-то более простой способ справиться с этим.