Это первая из коллекции статей, которые я написал о своем первом опыте создания серверного API в среде Elixir Phoenix. Я также писал сообщения о заглушках, авторизациях, ассоциациях и пользовательских проверках. Попутно я узнал несколько небольших указателей, когда дело дошло до тестирования, маршрутизации и других вещей, которые, как мне кажется, полезно знать всем, кто набирает обороты на Phoenix.

Недавно я впервые реализовал OAuth в Phoenix, почти полностью руководствуясь этим руководством. Сначала я попробовал пару других руководств, и все пошло не так хорошо - я настоятельно рекомендую этот, потому что он внимателен к деталям и тот факт, что он прошел тестирование. Из-за этих добродетелей я не собираюсь повторять весь процесс; вместо этого я хочу подчеркнуть некоторые важные моменты и подробно остановиться на тех моментах, в которых моя реализация разошлась из-за того, что я был предназначен для внутреннего API. Я отмечу, что есть некоторые различия в наших User ресурсах, которые должно быть достаточно легко идентифицировать в коде.

Одна из самых мелких частей настройки аутентификации с помощью Ueberauth также была самой большой загвоздкой, с которой я столкнулся во всем процессе. Это связано с настройкой Ueberauth в файле config/config.exs:

Внутри нашего config/config.exs мы начнем процесс настройки нашего приложения для аутентификации в Google. Это будет над линией import_config “#{Mix.env}.exs”

После нескольких попыток заставить OAuth работать с помощью других руководств, это оказалось моей большой оплошностью. Учебник Эмори был единственным, с которым я столкнулся, в котором явно упоминалась конфигурация, которая должна быть выше строки import_config “#{Mix.env}.exs”. Это может быть очевидно для людей, более опытных с Эликсиром, чем я, но эта ошибка стоила мне часов головной боли, потому что вывод ошибки, который следует из нее, почти бесполезен для определения реальной проблемы. Чтобы еще раз подчеркнуть, нижняя часть файла config/config.exs должна выглядеть примерно так:

Большие различия между моей реализацией и реализацией в этом руководстве связаны с тем, что я реализовал ее в API, который будет независимо развернут в качестве серверной части для приложения React. Мой тест выявил некоторые из этих различий, поэтому давайте сначала рассмотрим их:

Первое отличие может быть незначительным - мне не нравится предположение о том, что начальное количество пользователей равно 0, которое неявно присутствует в assert Enum.count(users) == 1. Если тестовая база данных удаляется при очистке - а это должно быть, учитывая настройку проекта Phoenix по умолчанию - тогда он должен быть 0 в начале каждого тестового прогона, но предположение просто кажется мне хрупким и меня неправильно путь немного. Кроме того, я думаю, что assert Enum.count(users) == initial_user_count + 1 более четко указывает на тот факт, что мы тестируем, что общее количество пользователей было увеличено, улучшая читаемость тестов.

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

Следующее и последнее утверждение проверяет, что идентификатор сеанса был успешно отправлен в качестве файла cookie ответа. Это позволит нам аутентифицировать последующие запросы, связанные с данным сеансом (процесс, о котором я расскажу в другом посте). Вот мое результирующее действие контроллера:

Большие различия здесь напрямую коррелируют с этими дополнительными утверждениями в моем тесте - чтобы аутентифицировать запросы, поступающие из внешнего интерфейса (или где-то еще в этом отношении), мне нужно будет сравнить файл cookie с чем-то, что хранится в моем сеансе, поэтому я m хранит токен в сеансе и отвечает файлом cookie, хранящим тот же токен, перед перенаправлением во внешнее приложение. Обратите внимание, что перенаправление включает UUID пользователя в качестве параметра url, так что интерфейс может использовать его для запроса информации этого пользователя. Поле UUID в пользовательском ресурсе также специфично для моей реализации - вместо того, чтобы отображать серийный идентификатор, хранящийся в качестве первичного ключа пользователя в базе данных, я использую UUID, чтобы затруднить использование открытого идентификатора для вывода информации о другой пользователь. Я также добавляю это в сеанс под :user_uuid, а не под :user_id.

Мне еще предстоит многое узнать о Phoenix, Ecto и Elixir, но внедрение OAuth научило меня многому. Следующие уроки были связаны с созданием уровня аутентификации / авторизации для проверки законности запросов и авторизации пользователей, выполняющих эти запросы. Это потребовало ознакомления с использованием и механикой плагинов, сеансов и файлов cookie в Phoenix. Хотя это совсем другой пост.