Часть 3: Невеста

Это третья статья из этой серии, в которой объясняется, как настроить Dgraph с Auth0 для нашего демонстрационного проекта. Если вы не настроили ни Dgraph, ни Auth0, вы можете прочитать Часть 1 и/или Часть 2 этой серии. Однако если вас интересует только надлежащая защита вашей конечной точки GraphQL, вы можете смело пропустить эту часть.

Введение

В последних двух частях этой серии мы настроили наш облачный API (Dgraph) и нашу службу аутентификации (Auth0) для нашего демонстрационного приложения Quote. Хотя у нас есть служба аутентификации, наша конечная точка GraphQL по-прежнему не защищена, поскольку Dgraph еще ничего не знает об Auth0. Установление связи между этими сервисами и является целью данной статьи.

Резюме

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

// Quote
type Quote {
 id: ID!
 text: String!
 author: Author!
}
// Author
type Author {
 id: ID!
 name: String!
}

Мы также создали учетную запись Auth0 и настроили

Поскольку Dgraph должен знать, что мы хотим защитить нашу конечную точку GraphQL через Auth0, нам нужно назначить ключ нашего приложения (ключ, который используется для подписи всех токенов JWT, которые мы получим от Auth0) нашей схеме.

Создайте ключ проверки из певческого сертификата приложения Quote

Давайте войдем в нашу панель управления Auth0 и перейдем к Applications > Quote App и прокрутим до дополнительных настроек. Откройте раскрывающийся список, выберите вкладку «Сертификаты», нажмите «Загрузить сертификат» и выберите «PEM».

Теперь у вас должен быть файл с *.pem в папке загрузок. Файл Privacy Enhanced Mail включает в себя открытый ключ, который нам нужно будет извлечь на следующем шаге. Я работаю с MacOS (для систем Linux следующее идентично) — для Windows могут быть другие команды.

Откройте консоль и перейдите в папку загрузок, содержащую файл PEM. Входить

openssl x509 -pubkey -noout -in <your-file-name>.pem | awk '{printf "%s\\n", $0}'

и скопируйте полученную строку до \n% в конце.

Обратите внимание на \n в строке! Они важны, потому что Dgraph запрашивает одну строку в своем пакете данных JSON.

Примените открытый ключ Auth0 к заголовку авторизации Dgraph

Мы почти закончили!

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

  • Ключ проверки → это открытый ключ, который мы только что создали.
  • Заголовок → это может быть что угодно. Для нашего приложения давайте установим его в Quote-Auth
  • Пространство имен → Для всех конкретных запросов Dgraph это может быть что угодно, поэтому давайте установим его на https://quote-app-claims (только убедитесь, что у вас есть действующий URL-адрес!)
  • Algo → алгоритм, которым был подписан ключ. Поскольку мы используем открытый ключ, для него установлено значение RS256.
  • Аудитория → на самом деле это множество аудиторий. Пока это наш идентификатор клиента приложения Quote, позже, когда у нас будут вызовы между машинами, мы добавим сюда дополнительную аудиторию.

Итак, как нам теперь добавить все это в Dgraph? Ну, Dgraph делает это очень простым для нас. Dgraph принимает простую строку вида

# Dgraph.Authorization {"VerificationKey":"<AUTH0-PUBLIC-KEY>","Header":"Quote-Auth","Namespace":"https://quote-app-claims","Algo":"RS256","Audience":["<AUTH0-QUOTE-APP-CLIENT-ID>"]}

Нам просто нужно добавить эту строку в конец нашей схемы. Поэтому войдите в свою панель инструментов Dgraph, нажмите Schema и скопируйте и вставьте строку.

Не забудьте заменить <AUTH0-PUBLIC-KEY> и <AUTH0-QUOTE-APP-CLIENT-ID> своим ключом/идентификатором.

Идентификатор клиента приложения Quote можно найти на панели инструментов Auth0 в разделе Applications > Quote App.

Разверните схему, и все готово!

Тестирование защиты конечных точек

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

По этой причине я лично использую Altair GraphQL Client (это тоже бесплатно 🙌), но вы также можете использовать Postman или любой другой инструмент, который вам нравится, для имитации запросов GraphQL к вашей конечной точке.

Во-первых, давайте запросим то, что уже хранится в нашей базе данных. Если вы следили за Частью 1 этой серии, у вас уже должна быть цитата и автор, сохраненные в вашей базе данных. Сначала нам нужно войти в нашу конечную точку GraphQL (доступную на вкладке Обзор панели инструментов Dgraph) и запустить самоанализ. Теперь Altair получит вашу схему из Dgraph со всей информацией, необходимой для отправки запроса на вашу конечную точку. Затем мы можем ввести запрос котировки в поле запроса

query QueryQuotes {
  queryQuote {
    id
    text
    author {
      name
    }
  }
}

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

Без требований или дизайна программирование — это искусство добавления ошибок в пустой текстовый файл. — Луис Сригли

Тогда мутация:

mutation AddQuoteWithAuthor {
  addQuote(
    input: {
      text: "Without requirements or design, programming is the art of adding bugs to an empty text file."
      author: {
        name: "Louis Srygley"
      }
    }
  ) {
    numUids
  }
}

Поэтому добавьте эту мутацию в поле запроса в клиенте GraphQL и запустите мутацию. Если все прошло хорошо, вы должны увидеть numUids: 2 в разделе данных объекта ответа.

Если повторно запустить запрос цитаты, мы должны увидеть две цитаты:

Все идет нормально. Так что на данный момент каждый может запрашивать котировки, а также ДОБАВЛЯТЬ цитаты в наше приложение! Это неприемлемо — мы можем получить кавычки, которые на самом деле не хотим видеть в нашем приложении. Мы хотели бы, чтобы пользователи нашего приложения запрашивали цитаты, но только администраторы должны иметь право добавлять цитаты.

К счастью, Dgraph делает это очень простым для нас, используя встроенную в Dgraph директиву @auth. Поэтому мы переходим к нашей панели инструментов Dgraph на вкладке Schema и добавляем следующие строки в наш тип котировок.

type Quote @auth(
  add: { rule:  "{$isAdmin: { eq: \"true\" } }"}
  update: { rule:  "{$isAdmin: { eq: \"true\" } }"}
  delete: { rule:  "{$isAdmin: { eq: \"true\" } }"}
)
{
  id: ID!
  text: String!
  author: Author!
}

Теперь это означает, что только пользователи с полем isAdmin: “true” в своем токене смогут добавлять, обновлять или удалять котировки в нашем приложении Quote. После развертывания схемы попробуйте повторно запустить мутацию AddQuoteWithAuthor — вы должны увидеть addQuote: null в теле данных вашего результирующего объекта.

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

Опять же, перейдите на панель инструментов Dgraph и добавьте это в свой тип автора на вкладке GraphQL Schema и добавьте директиву @generate:

type Author 
@generate(
  query: { query: false, get: false, aggregate: false }
  mutation: { add: false, update: false, delete: true }
)
@auth (
  delete: { rule:  "{$isAdmin: { eq: \"true\" } }"}
)
{
  id: ID!
  name: String!
}

Поскольку мы оставили delete: true (мы по-прежнему хотим иметь возможность удалять авторов, если, например, все цитаты автора удалены), мы также добавляем еще одно правило @auth к типу Author.

Кроме того, перейдите к Access > Edit Permissions и удалите разрешения на чтение и запись для типа Author.

Герат! Если вы следили за частью 2 этой серии, вы уже создали пользователя-администратора на панели инструментов Auth0. Теперь нам нужно создать утверждение пользователя, которое должно включать поле isAdmin: “true" для пользователей, которые входят в наше приложение через Auth0.

Поэтому перейдите на свою панель управления Auth0 и перейдите к User Management > Users и выберите пользователя-администратора, которого вы хотите использовать с приложением Quote (для меня это [email protected]). Прокрутите вниз до раздела Metadata и введите

{
 "isAdmin": "true"
}

Обратите внимание, что isAdmin помечен как строка. Это связано с тем, что Dgraph может проверять только строки из полей в утверждении токена.

Затем перейдите к Actions > Flows и выберите процесс входа. Щелкните значок ➕ рядом с «Добавить действие» и выберите «Создать пользовательское».

Дайте вашему действию броское имя — я использовал Create User Claim и нажмите «Создать».

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

// refactoring
const appMeta = event.user.app_metadata;
// set custom claim
api.idToken.setCustomClaim("https://quote-app-claims", {
  "isAdmin": appMeta.isAdmin
});

Разверните свое действие, нажмите «Назад», чтобы перейти к потоку в верхнем левом углу, найдите свое пользовательское действие на вкладке Custom и просто перетащите действие между «Начало» и «Завершение». Нажмите «Применить», и все готово! 👌

Давайте проверим, что мы создали здесь. Во Части 2 этой серии я создал пример CodeSandbox, чтобы проверить нашу настройку Auth0. Теперь мы хотим расширить этот пример, извлекая наш необработанный токен JWT после входа в систему и проверяя, правильно ли установлено наше пользовательское утверждение.

Вот ссылка на мой пример CodeSandox — не стесняйтесь использовать его в качестве стартового!

SDK Auth0 предоставляет удобную небольшую функцию, которая позволяет вам извлекать необработанный JWT — метод getIdTokenClaims. После того, как мы успешно вошли в наше приложение, мы можем скопировать наш JWT и проверить, что внутри, например. jwt.io.

Чтобы убедиться, что наш новый токен действительно работает, мы теперь хотим использовать его для выполнения запросов к нашей конечной точке Dgraph GraphQL.

Таким образом, скопируйте JWT, откройте свой клиент GraphQL (для меня это Altair), щелкните значок заголовка и добавьте заголовок Quote-Auth с JWT в качестве значения.

С этим набором вы можете повторно запустить мутацию (давайте добавим третью цитату) и посмотреть, все ли работает.

mutation AddQuoteWithAuthor {
  addQuote(
    input: {
      text: "There are two ways to write error-free programs; only the third one works."
      author: {
        name: "Alan J. Perlis"
      }
    }
  ) {
    numUids
  }
}

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

Спасибо за прочтение.