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

Для взаимодействия с провайдером OAuth необходимы ключи API. По соображениям безопасности эти ключи должны храниться на сервере закрытыми. Однако, если вы следуете современным методам разработки, ваше приложение React, скорее всего, полностью отделено от вашего сервера и работает на другом порту или в другом домене.

Итак, как мы можем устранить этот разрыв связи между поставщиком OAuth, которому нужны ключи, и клиентом React, который не может сохранить эти ключи в секрете?

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

(Очень) краткая история аутентификации

Если вы раньше работали с аутентификацией OAuth, эта диаграмма покажется вам знакомой:

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

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

Но с современной архитектурой приложения, теперь отделяющей клиента от сервера (или служб), с которого он получает данные, как может клиент React, который не может сохранить закрытые ключи API, аутентифицироваться с поставщиком OAuth, которому нужен закрытый ключ API?

Есть решения этой проблемы.

Некоторые, такие как Firebase, пропитаны черной магией и абстрагируются от всей внутренней работы. Другие, такие как React Auth Twitter, кажутся излишне спроектированными (см. Рабочий процесс ниже).

Множество ретрансляторов, где что-то пойдет не так, конечно, должен быть лучший способ ...

Я считаю, что хороший подход - использовать сокеты:

Здесь клиент связывается с сервером через запрос на запуск процесса аутентификации (2) и через ответ сокета, чтобы в конечном итоге получить информацию пользователя (5).

К счастью, из-за многоразового использования компонентов React и совпадения стратегий PassportJS это может произойти с небольшим количеством кода для нескольких поставщиков OAuth, включая Twitter, Facebook, Google, Github и многие другие.

Давайте посмотрим, как это осуществить.

Клиент

Этот проект загружается с помощью Create React App. Я предполагаю, что у вас уже есть некоторый опыт работы с CRA. Если нет, то вот отличный учебник.

Первый файл, который мы рассмотрим, - это точка входа для любого приложения React, App.js:

Обратите внимание, что сокет подключается в родительском компоненте App.js в строке 6, а затем передается дочерним компонентам OAuth.js в качестве опоры. в строке 19.

Переменная провайдеры - это просто массив строк с именами провайдера OAuth. Это хорошие новости! Добавить другого провайдера на клиенте так же просто, как включить соответствующую строку в наш массив.

Супер расширяемый.

Затем давайте взглянем на дочерний компонент OAuth.js:

OAuth.js - это основа клиента, поэтому мы разберем его на части, чтобы понять каждую часть.

Нам нужно немного состояния, чтобы помочь управлять тем, показывать ли пользователю или отображать кнопку входа в систему. В строке 15 мы также прослушиваем любой ответ через сокеты от сервера, который соответствует нашей опоре "provider" в componentDidMount.

Затем мы должны настроить пользовательские методы в классе OAuth, чтобы запустить процесс аутентификации и гарантировать, что состояние обновляется соответствующим образом, если пользователь прерывает процесс аутентификации.

Здесь есть небольшая функциональность, поэтому давайте рассмотрим ее более подробно.

checkPopup () - каждую секунду проверяет всплывающее окно, чтобы повторно включить кнопку входа в систему, если пользователь закрывает всплывающее окно без аутентификации.

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

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

closeCard () - Я уверен, что вы понимаете, что это делает :)

Теперь, когда у вас есть все необходимые функции, давайте посмотрим на метод render ():

Не слишком много для этого.

Состояние пользователя используется для выборочного отображения либо его социального профиля, если он существует, либо соответствующей кнопки входа в систему, чтобы пользователь мог войти в систему. Используя font awesome и немного CSS, мы создаем аутентичные кнопки, используя свойство provider в качестве className.

Это все для клиента. Пора переходить к серверу.

Сервер

Поскольку на сервере мы собираемся хранить ключи API, нам нужно будет получить эти ключи от поставщиков, с которыми мы работаем. Вот инструкции, как получить ключи от Twitter, Google, Facebook и Github.

Убедитесь, что URL-адрес обратного вызова, который вы указываете при регистрации приложения, находится в форме:

https://localhost:8080/__provider__/callback

После регистрации для каждого провайдера нам нужно вставить эти ключи в файл .env на сервере. Если вам нужна небольшая помощь, пожалуйста, прочтите это.

Теперь мы готовы перейти к написанию сервера:

Server.js довольно легкий, поскольку большая часть функций приложения будет перенесена в другие файлы.

Следует отметить, что приложение запускается в режиме HTTPS в строке 22 (подробнее об этом ниже) и что сокеты устанавливаются непосредственно в приложении в строке 45 . Прикрепив сокет к приложению, сокет может быть доступен в маршрутах в будущем, когда нам понадобится общаться с клиентом.

Переходим к настройке PassportJS:

Мы привлекаем стратегии для различных поставщиков, с которыми работаем, и добавляем их в singleton Passport, чтобы их можно было использовать во всем приложении. Если вы работали с Passport раньше, это вам покажется знакомым.

Если вы раньше не работали с Passport, это отличный урок.

Что немного необычно выше, так это то, что обратный вызов в строке 21 одинаков для всех поставщиков OAuth. Обычно этот обратный вызов - это то место, где вы сохраняете пользователя в базе данных, и его нужно настраивать для каждого поставщика индивидуально.

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

Когда Passport настроен и готов к работе, давайте взглянем на маршрутизатор:

У нас есть некоторые настройки для включения промежуточного программного обеспечения PassportJS, а затем мы написали небольшой фрагмент промежуточного программного обеспечения в строке 17, чтобы прикрепить идентификатор сокета подключенного клиента, который входит в req.query, к req.session.

addSocketIdToSession - прикрепляет идентификатор сокета к сеансу и позволяет серверу отправлять обратно правильную информацию о пользователе в правильный сокет после завершения процесса аутентификации с поставщиком OAuth.

Мне нравится разбивать маршруты и обратные вызовы на разные файлы на сервере, чтобы код был модульным. Но предыдущий файл и следующий файл можно было объединить.

Пора взглянуть на следующий (и последний!) Файл:

Поскольку мы использовали app.set (‘io’, io) в server.js, теперь у нас есть доступ к сокетам из любой точки приложения, имеющей запрос в области видимости. Это очень удобно, поскольку теперь мы можем принимать обратный вызов от поставщика OAuth, а затем передавать информацию о пользователе клиенту через сокеты.

Хотя этот код довольно часто повторяется, в какой-то момент нам нужно создать объект пользователя для отправки клиенту, и каждый поставщик OAuth отправляет свои данные в другой форме. Учитывая это, похоже, нет способа избежать ненужного шаблона.

‹Примечание на стороне› Обычно я бы переместил такие функциональные возможности конфигурации в обратные вызовы функции паспортInit, как упомянуто выше, и переместил бы пользователя в базу данных в этот момент времени. Но это казалось более простым, учитывая, что мы не сохраняем никаких пользовательских данных в этом руководстве.

Вот и все!

Сервер не так легко расширяем, как клиент, но мы создали твердую схему, в которой добавление дополнительного поставщика OAuth требует установки / настройки пакета PassportJS для этого поставщика, настройки пары маршрутов и написания метод контроллера.

Не плохо.

HTTPS

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

И… возможно, вы захотите выпить чашку кофе, прежде чем мы начнем. Это может занять жаркую минуту.

Из-за требования Facebook о том, что любое приложение, взаимодействующее с API Facebook (даже в стадии разработки), должно обслуживать запросы через HTTPS, нам потребуется запускать и клиент, и сервер в режиме HTTPS.

Для клиента create-response-app это так же просто, как запустить сервер разработки с помощью этой команды:

HTTPS=true react-scripts start (OS X)
or
set HTTPS=true&&npm start (Windows)

Ваше клиентское приложение теперь запущено с HTTPS. Однако безопасный клиент может делать запросы только к защищенному серверу, и эта настройка немного сложнее.

Ниже приведены инструкции OS X по настройке и запуску локального сервера и сертификатов HTTPS:

  1. Как заставить HTTPS работать в вашей локальной среде разработки за 5 минут
  2. Импорт и экспорт сертификата электронной почты или персональной аутентификации с помощью Chrome в Mac OS X
  3. Как мне работать с NET: ERR_CERT_AUTHORITY_INVALID в Chrome?

Таким образом, процесс состоит в том, чтобы создать сертификаты на вашем локальном компьютере, а затем сообщить вашему компьютеру, что можно принимать эти самозаверяющие сертификаты.

Я сам не сторонник Windows, поэтому не пробовал этого ... Но вот видео о том, как настроить HTTPS на локальном сервере Windows, выглядит довольно неплохо.

И спасибо Le Gui PPF за предоставление следующих инструкций по настройке SSL локально в Windows:

Привет, спасибо за код! Я еще не пробовал, потому что потратил целый день (горячая минута, да!), Выясняя, как сгенерировать правильный ssl-сертификат со всеми требованиями Chrome… для тех, кто не хочет терять свое время = ›« https: // serverfault.com/a/850961 и добавьте

«echo AuthorityKeyIdentifier = keyid, издатель
echo basicConstraints = CA: FALSE
echo keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment»

до v3 req.

Надеюсь, это сработает для вас, ребята, Windows!

Я знаю, что настройка локального сервера на HTTPS может быть проблемой, если вы боретесь с этими инструкциями, подумайте о том, чтобы начать только с Twitter (без HTTPS).

Выводы

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

Эта установка работает, потому что компоненты React можно использовать повторно, и в стратегиях PassportJS существует огромное количество совпадений. После того, как он будет запущен и запущен, его можно расширить, добавив дополнительных поставщиков OAuth с небольшими накладными расходами.

Так что вы думаете об этом рабочем процессе? Есть ли у вас другая стратегия аутентификации OAuth с помощью React? Я хотел бы услышать об этом.

Если этот урок вам хоть как-то помог, все, о чем я прошу, это поднять пятьдесят (+50) на кнопку хлопка.

Спасибо :)





✉️ Подпишитесь на рассылку еженедельно Email Blast 🐦 Подпишитесь на CodeBurst на Twitter , просмотрите 🗺️ Дорожная карта веб-разработчиков на 2018 год и 🕸️ Изучите веб-разработку с полным стеком .