Аутентификация и авторизация в GraphQL (и как могут помочь GraphQL-модули)

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

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

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

Аутентификация / авторизация?

Я обнаружил, что хороший ответ в StackOverflow охватывает основные различия между аутентификацией и авторизацией:

В этой статье я расскажу о разнице между аутентификацией и авторизацией с помощью API-интерфейсов GraphQL, объясню, как их реализовать с помощью сервера GraphQL и фреймворка GraphQL-Modules.

Мы узнали, что хорошая реализация аутентификации GraphQL имеет следующие особенности, касающиеся аутентификации:

  • Ваша реализация аутентификации должна в конечном итоге предоставить currentUser преобразователям
  • Ваши преобразователи не должны знать о логике аутентификации (разделение между бизнес-логикой и логикой аутентификации).
  • Вы хотите защитить только часть своей схемы GraphQL, а не всю ее.
  • Вы хотите иметь возможность аутентифицировать части вашей схемы на field уровне.

И следующие особенности, касающиеся авторизации:

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

Куда поставить вашу аутентификацию?

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

Запрос приходит из сети, проходит через HTTP-сервер (например, express) - это №1 в диаграмме.
Затем он проходит через сервер GraphQL, который создает context
, а затем запускает ваш resolvers - # 2.
Затем у вас есть бизнес-логика, которую вы, возможно, захотите отделить от преобразователей, это # 3 .

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

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

Итак, возьмем №1:
Если вы реализуете аутентификацию на №1 (между сетевым запросом и HTTP-сервером), вы, вероятно, делаете это с помощью express промежуточного программного обеспечения. или что-то подобное.
Преимущество этого заключается в разделении логики аутентификации и бизнес-логики. Но вы не можете подключить свои данные из HTTP-запроса к серверу GraphQL, и впоследствии может быть сложно получить доступ к currentUser. Он также защищает всю конечную точку GraphQL, а не только ее части.
Это хорошо, но не идеально.

Что касается №3:
Внедрение аутентификации как часть кода преобразователя или кода бизнес-логики - не лучшая идея. Обычно это означает, что код вашего приложения слишком много знает об аутентификации и, вероятно, связан с ней.
Это может быть проще реализовать, но гораздо сложнее использовать позже.

И, №2:
Вы можете реализовать аутентификацию между вашим HTTP-сервером и вашим GraphQL-сервером с помощью GraphQLcontext.
Реализуя пользовательскую функцию построения context, вы можете получить доступ к сетевой запрос, создайте свой context объект и добавьте к нему currentUser
. Затем ваши resolvers будут вызваны движком GraphQL.

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

В следующей главе мы погрузимся в подробности и посмотрим, насколько легко это реализовать с помощью Apollo-Server.

Начало работы с аутентификацией

Начнем с реализации простого процесса аутентификации на сервере GraphQL (с использованием Apollo-Server 2.0).

Я предполагаю, что у вас уже есть свой любимый способ аутентификации, будь то заголовки, файлы cookie или любой другой способ. С точки зрения сервера, мы ожидаем получить токен и для простоты предположим, что он исходит из заголовков.

Давайте начнем с создания простого экземпляра Apollo-Server с очень простой схемой. Мы сосредоточимся на отображении currentUser как поля запроса.

Теперь давайте реализуем context и посмотрим, сможем ли мы получить действительного пользователя из значений, которые мы получили.
Наша цель в этой части - только проверить токен и попытаться обменять его на пользователя.
Если токен или пользователь недействительны - мы не хотим создавать здесь исключение, потому что в этом случае наш сервер не будет поддерживать общедоступные запросы.
Итак, мы просто пытаемся получить пользователя и токен , и верните их Apollo-Server, чтобы он использовал его как context для наших преобразователей. Если что-то пошло не так с проверкой - мы просто возвращаем null для user и authToken.

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

Теперь мы, наконец, можем реализовать resolvers для сервера:

Это наивная реализация, предполагающая, что токен действителен, потому что в противном случае context.currentUser будет null.
Это подводит нас к следующим шагам: реализация охранников.

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

Итак, давайте реализуем простую защиту и будем использовать ее на нашем сервере.
Я также добавил общедоступную часть схемы (Query.serverTime), которая должна быть общедоступной и не должна зависеть от потока аутентификации. Значит, это не наша охрана.

Большой! Итак, теперь наш сервер аутентифицирован, и мы можем получить currentUser в наших преобразователях.
Он также защищает только ту часть схемы GraphQL, которую мы хотели защитить, а поток аутентификации отделен от преобразователей, поэтому каждый преобразователь выполняет только то, что ему нужно.

Начало работы с авторизацией

Теперь, когда мы понимаем концепцию аутентификации на серверах GraphQL, мы также можем реализовать авторизацию.

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

Давайте добавим type Article в схему и разрешим создавать статьи только пользователям, для которых role установлено значение EDITOR. Это так просто:

Реализация следующего уровня с GraphQL-модулями

Если вы хотите вывести свой GraphQL-сервер на новый уровень и создать масштабируемый, тестируемый и читаемый сервер, я рекомендую попробовать GraphQL-Modules.

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

Он также очень мощный, потому что вы можете реализовать свой GraphQL-модуль как отдельный модуль, использовать его в нескольких приложениях, а затем применять разные правила аутентификации / авторизации в каждом приложении.

Чтобы реализовать приведенный выше пример с модулями GraphQL, вы можете создать модуль с именем auth и переместить туда context логику построения.
Затем создайте еще один модуль с именем common и общие части и articles для управления функциями статей. .

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

Будущее аутентификации с помощью GraphQL-модулей и @directives GraphQL

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

Для начала добавьте следующие @directives в схему вашего auth модуля:

Затем вы можете легко преобразовать свой resolversComposition в функцию, которая принимает GraphQLSchema, а затем проверить, используя свой объект схемы, который решает, какая логика требуется, и выполнить сопоставление:

Затем вы добавляете директивы в объявление схемы:

Что дальше?

Еще одна вещь, над которой мы работаем, - это установить часть вашей логики повторной аутентификации и авторизации через npm!

Мы работаем с создателями библиотек js-accounts над созданием модуля GraphQL Modules из их замечательных пакетов, чтобы вы могли просто добавить его из npm и расширить свой сервер и схему.

Вы можете прочитать это сообщение в блоге, чтобы научиться использовать эту библиотеку с GraphQL-модулями за несколько шагов;
https://medium.com/the-guild/authentication-with-accountsjs-graphql-modules-e0fb9799a9da

Аутентификация и авторизация - фундаментальная часть вашего сервера GraphQL.

Он должен быть простым и безопасным для реализации и понимания того, как это сделать, чтобы способствовать развитию сообщества GraphQL.

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

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

Все сообщения о модулях GraphQL

  1. Модули GraphQL - Масштабные модули GraphQL на основе функций
  2. Почему истинная модульная инкапсуляция так важна в крупномасштабных проектах GraphQL?
  3. Почему мы реализовали собственную библиотеку внедрения зависимостей для GraphQL-модулей?
  4. Провайдеры с ограниченной областью видимости во внедрении зависимостей GraphQL-модулей
  5. Написание проекта GraphQL TypeScript с GraphQL-модулями и GraphQL-Code-Generator
  6. Аутентификация и авторизация в GraphQL (и как могут помочь GraphQL-модули)
  7. Аутентификация с помощью модулей AccountsJS и GraphQL
  8. Управляйте адским циклическим импортом с помощью GraphQL-модулей