Это 13-я часть моей серии React Learning. Использование знаний, почерпнутых из React Веса Боса для начинающих, среди прочего.

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

Authentication - Checking to see who someone is

Authorisation - Controlling what can be accessed based upon who they are

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

  • Меню — где пользователи могут выбирать, что заказывать
  • Заказ — где пользователи могут увидеть, что они заказали.
  • Инвентарь — который диктует, что доступно для заказа

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

В этом приложении мы хотим ограничить инвентарь владельцем. Это то, что мы собираемся сделать в этом посте, используя аутентификацию Firebase для входа в GitHub.

Большие темы быстро запутываются, поэтому тема этого поста — разбить все на более мелкие шаги…

  1. Настройте поставщиков аутентификации с помощью Firebase.
  2. Создайте наш компонент обработки входа в систему и создайте метод authenticate для запроса аутентификации у соответствующего поставщика входа.
  3. Создайте метод «authhandler», чтобы делать что-то, как только мы получим результаты метода аутентификации.
  4. Настройте логику в компоненте Inventory для отображения соответствующего компонента авторизованным пользователям.
  5. Внедрите правила Firebase, чтобы эффективно заблокировать его в серверной части.

Итак, приступим…

Часть 1. Настройка наших поставщиков аутентификации

Этот раздел посвящен настройке серверной части, в нашем случае серверной частью является Firebase и сервисы, с которыми мы хотим пройти аутентификацию. Мы не будем заниматься созданием собственной аутентификации, так как это быстро запутается!

Для каждого поставщика услуг входа есть два основных шага:

  • Получите ключи API и настройте параметры на сайте разработчика поставщика услуг входа.
  • Настройте Firebase с данными API от поставщика входа

Примечание о получении ключей API

На самом деле я написал разделы о получении ключей API для аутентификации Facebook, GitHub и Twitter. С момента написания (2018 г.) они изменили способ работы. Что еще более важно, они, вероятно, изменят его снова к тому времени, когда вы будете читать это, так что вам лучше понять это из их собственной документации, а не следовать старым советам. Я, по крайней мере, включу GitHub как довольно простой пример того, что мы пытаемся сделать.

Ключ API GitHub

  1. Перейдите в вашу консоль Firebase и перейдите к Authentication
  2. Выберите Github внутри set up sign in method
  3. Теперь ему понадобятся App ID и App Secret. Также обратите внимание, что он предоставляет URL для возврата после попытки аутентификации.
  4. Перейдите к [https://github.com/settings/developers]
  5. Зарегистрируйте новое приложение OAuth и вставьте URL-адрес обратного вызова авторизации из firebase.
  6. Идентификатор клиента и секрет клиента теперь будут доступны для добавления в Firebase.

Часть 2 — Компонент входа и метод аутентификации

Итак, давайте рассмотрим настройку приложения для отправки запроса на вход. АКА Аутентификация. АКА «Кто ты?»

Нам нужно предоставить код для общения с Firebase. Это традиционно на странице входа в систему.

Основные шаги для этого:

  1. Настройте компонент входа
  2. Настройте родительский компонент инвентаризации с помощью метода аутентификации.
  3. Передайте метод аутентификации в логин через реквизит
  4. Настройте компонент входа для использования метода аутентификации

Настройка и тестирование компонента входа в систему

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

В нашем компоненте Inventory нам нужно проверить, видим ли мы кнопку. Сначала импортируйте наш компонент входа:

import Login from "./Login"

А затем вставьте еще один возврат в начале метода рендеринга, эффективно перенаправляя компонент Inventory для отображения компонента входа в систему только на данный момент:

return <Login />

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

Заставить кнопку что-то делать… почти

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

Итак, наша кнопка должна:

  1. Вызовите метод в свойствах с именем authentication. Этого нет, но скоро будет.
  2. Предполагая, что у нас будет более одного типа кнопки входа… в качестве параметра для этого метода мы возьмем имя провайдера с заглавной буквы. Почему капитал? Я объясню это тоже немного позже, обещаю.

Теперь наша кнопка входа на GitHub в компоненте входа выглядит так:

<button onClick={() => props.authenticate('GitHub')}> Sign in with GitHub </button>

Обратите внимание, что в функциональном компоненте мы ссылаемся на реквизиты, передавая параметр, а не используя this.

Также не забудьте добавить PropTypes, например:

Login.propTypes = { authenticate: Proptypes.func.isRequired}

Создание метода аутентификации

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

Во-первых, убедитесь, что вы импортировали FireBase, иначе мало что произойдет, в Firebase есть методы, которые нам нужны, чтобы упростить эту задачу:

import firebase from 'firebase';

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

import {firebaseApp} from '../base'

  1. Для метода мы передаем значение с кнопки как Provider (т.е. «GitHub»). Это умный способ проверить провайдера регистрации.
  2. Указываем нашего провайдера авторизации
  3. Мы используем методы Firebase для создания всплывающего окна, которое использует поставщика аутентификации, указанного для запроса входа в систему.

Код выглядит примерно так:

Часть 3. Обработка ответа аутентификации

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

Если пока все шло хорошо. Теперь вы сможете войти в GitHub и увидеть объект, возвращенный в консоль. Этот объект содержит всю необходимую нам информацию.

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

В нашем случае использования authhandler необходимо сделать три вещи:

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

1. Найдите текущий магазин в базе данных Firebase.

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

import base, {firebaseApp} from '.../base'

Затем нам нужно получить информацию о текущем магазине. Но подождите…, сначала нам нужно, чтобы компонент Inventory знал storeid, который мы можем получить от родительского компонента App (который сам получает его от React Router):

storeId ={this.props.match.query.storeId}

Итак, вернемся к нашему методу authHandler в компоненте Inventory для получения хранилища из Firebase:

const store = await base.fetch( *STORE GOES HERE* )

Чтобы получить имя магазина, нам нужно передать реквизит из приложения:

storeId={this.props.match.params.storeId}

Итак, готовая команда в authhandler выглядит так:

const store = await base.fetch(this.props.storeId, {context: this})

Если мы не используем await здесь, store будет обещанием, а не результатом обещания, которое нам нужно.

2. Заявить право собственности, если нет текущего владельца

Далее нам нужно проверить, есть ли владелец, если нет, мы сохраняем информацию о владельце в БД Firebase.

На этом этапе мы должны иметь возможность проверить в базе данных Firebase, установлен ли владелец.

3. Установите состояние компонента инвентаризации, чтобы отразить текущего пользователя

Чтобы решить, что делать, когда пользователь входит в систему, нам нужно знать две вещи:

  • Что такое UID вновь вошедшего в систему пользователя?
  • Кто является владельцем ресурса? Если есть?

Команда setState выглядит так:

Поскольку нам не нужна эта информация где-либо еще, мы можем установить State локально для компонента Inventory, а не для родительского компонента приложения. Для этого требуется настроить состояние компонента:

Весь метод authHandler выглядит так:

Если все прошло хорошо, вы сможете войти в систему и увидеть новые ключи в состоянии Inventory и значение владельца в хранилище Firebase DB.

Часть 4 — Отображение правильного контента

Методу рендеринга Inventory потребуется некоторая логика для проверки следующих сценариев:

  1. ЕСЛИ они НЕ вошли в систему, ТО показать компонент входа
  2. ЕСЛИ вы вошли в систему, а НЕ владелец, ТО показать «Сообщение об отсутствии доступа»
  3. ЕСЛИ вы вошли в систему И являетесь владельцем, ТО показать компонент инвентаря
  4. Помимо этого, мы также хотим сделать кнопку выхода из системы, чтобы разрешить другой вход в систему, если это необходимо.
  5. Наконец, мы не хотим входить в систему каждый раз при обновлении страницы, поэтому давайте автоматически перепроверим текущего пользователя.

1. Если НЕ вошел в систему, ТО показать компонент входа

UID, содержащийся в State, определяет, вошли ли они в систему, поэтому нам просто нужно вернуть компонент входа, если uid недоступен:

if (!state.uid) {return <Login authenticate={this.authenticate} />}

2. Вы вошли в систему, но НЕ являетесь владельцем, ТОГДА показать что-то типа «У вас нет доступа»

Достаточно просто, нам нужно сравнить uid в состоянии с владельцем в состоянии:

if (this.state.uid !== this.state.owner){return <div>You are not the owner</div>}

Вы, вероятно, захотите украсить это лучшим откликом, чем простой div, но пока этого достаточно.

3. Вы вошли в систему И являетесь владельцем, ТОГДА показать компонент инвентаризации.

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

4. Кнопка «Выйти»

Почти готово, в идеале это должен быть компонент, но для краткости мы определим переменную JSX внутри метода рендеринга:

const logout = <button onClick={this.logout}>Log Out</button>

Затем мы можем разместить кнопку на путях возврата «не владелец» и «владелец».

Метод выхода

Итак, давайте разберемся с методом выхода из системы, есть две задачи, которые необходимо выполнить во время выхода:

  1. Выйти из провайдера аутентификации
  2. Очистить состояние текущих сведений о пользователе

Это можно сделать в каждой строке:

https://gist.github.com/6e85d040afc6099bcc6a5f72f7899de9

5. Перепроверив, мы вошли в систему

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

  1. Получите Firebase, чтобы проверить, есть ли пользователь
  2. Передайте этого пользователя нашему методу authHandler

https://gist.github.com/e33a91f614ab1920a004b0abe3d530ca

Уф, об этом нужно много думать, но, надеюсь, вы видите, что, разбив его на маленькие шаги, это немного сводит с ума.

Часть 5 — Защита серверной части Firebase

Все, что мы сделали до сих пор, защитило клиентскую сторону, решительный человек все еще может получить доступ и изменить информацию в Firebase, поскольку мы оставили ее открытой для чтения и записи для всех (если вы следили за моим предыдущим постом о Firebase)

К счастью для нас, это довольно просто сделать:

  1. Перейти в Firebase
  2. Перейдите в раздел Database, а затем Rules

Это должно привести вас к разделу правил базы данных, где ранее мы разрешали чтение и запись как true:

Вместо этого нам нужно изменить это следующим образом:

Давайте объясним, что:

  1. Доступ для чтения разрешен для всех в любом месте.
  2. Пользователь может писать только на верхнем уровне (т. е. куда идет хранилище), если нет текущего хранилища (т. е. данные не существуют).
  3. В магазине ($room) запись разрешена только в том случае, если:
  • auth не является нулевым (пользователь вошел в систему)
  • Либо данные отсутствуют, либо существующий владелец совпадает с текущим.

Вывод

Я уже писал об аутентификации с помощью Passport JS. В целом это кажется немного более дружелюбным, поскольку Firebase берет на себя часть тяжелой работы. Кроме того, использование async и await позволяет избежать ада обратных вызовов, с которым я сталкивался раньше.

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

Разбить его на небольшие шаги — это определенно способ не сойти с ума, когда дело доходит до аутентификации.