Secure Data Connect с авторизацией и аттестацией

Firebase Data Connect обеспечивает надежную безопасность на стороне клиента с помощью:

  • Авторизация мобильных и веб-клиентов
  • Индивидуальный контроль авторизации на уровне запросов и мутаций
  • Аттестация приложений с помощью Firebase App Check .

Data Connect расширяет эту безопасность с помощью:

  • Авторизация на стороне сервера
  • Проект Firebase и безопасность пользователей Cloud SQL с помощью IAM.

Авторизация клиентских запросов и мутаций

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

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

Кроме того, Data Connect поддерживает выполнение запросов, встроенных в мутации, что позволяет извлекать дополнительные критерии авторизации, сохранённые в базе данных, и использовать их в директивах @check для определения авторизации включающих мутаций. В этом случае авторизации директива @redact позволяет контролировать, возвращаются ли результаты запроса клиентам по сетевому протоколу, а встроенный запрос исключается из создаваемых SDK. Ознакомьтесь с введением в эти директивы с примерами .

Понимание директивы @auth

Вы можете параметризовать директиву @auth , чтобы она соответствовала одному из нескольких предустановленных уровней доступа, охватывающих множество распространённых сценариев доступа. Эти уровни варьируются от PUBLIC (который разрешает запросы и изменения от всех клиентов без какой-либо аутентификации) до NO_ACCESS (который запрещает запросы и изменения вне привилегированных серверных сред, использующих Firebase Admin SDK ). Каждый из этих уровней коррелирует с потоками аутентификации, предоставляемыми Firebase Authentication .

Уровень Определение
PUBLIC Операцию может выполнить любой человек как с аутентификацией, так и без нее.
PUBLIC Операцию может выполнить любой человек как с аутентификацией, так и без нее.
USER_ANON Любой идентифицированный пользователь, включая тех, кто вошел в систему анонимно с помощью Firebase Authentication , имеет право выполнить запрос или мутацию.
USER Любой пользователь, вошедший в систему с использованием Firebase Authentication имеет право выполнять запрос или мутацию, за исключением анонимных пользователей, вошедших в систему.
USER_EMAIL_VERIFIED Любой пользователь, вошедший в систему с помощью Firebase Authentication и подтвержденного адреса электронной почты, имеет право выполнить запрос или мутацию.
NO_ACCESS Эту операцию невозможно выполнить вне контекста Admin SDK.

Используя эти предустановленные уровни доступа в качестве отправной точки, вы можете определить сложные и надежные проверки авторизации в директиве @auth , используя фильтры where и выражения Common Expression Language (CEL), оцениваемые на сервере.

Используйте директиву @auth для реализации распространенных сценариев авторизации.

Предустановленные уровни доступа являются отправной точкой для авторизации.

Уровень доступа USER — наиболее распространенный базовый уровень, с которого можно начать.

Полностью безопасный доступ будет реализован на уровне USER с фильтрами и выражениями, проверяющими атрибуты пользователя, атрибуты ресурсов, роли и другие проверки. Уровни USER_ANON и USER_EMAIL_VERIFIED являются вариациями уровня USER .

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

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

В этом руководстве теперь приводятся примеры того, как создавать проекты на основе USER и PUBLIC .

Мотивирующий пример

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

Такая платформа, скорее всего, будет моделировать Users и Posts .

type User @table(key: "uid") {
  uid: String!
  name: String
  birthday: Date
  createdAt: Timestamp! @default(expr: "request.time")
}

type Post @table {
  author: User!
  text: String!
  # "one of 'draft', 'public', or 'pro'"
  visibility: String! @default(value: "draft")
  # "the time at which the post should be considered published. defaults to
  # immediately"
  publishedAt: Timestamp! @default(expr: "request.time")
  createdAt: Timestamp! @default(expr: "request.time")
  updatedAt: Timestamp! @default(expr: "request.time")
}

Ресурсы, принадлежащие пользователям

Firebase рекомендует вам писать фильтры и выражения, которые проверяют право собственности пользователя на ресурс, в следующих случаях: право собственности на Posts .

В следующих примерах данные из токенов аутентификации считываются и сравниваются с помощью выражений. Типичный шаблон — использование выражений вида where: {authorUid: {eq_expr: "auth.uid"}} для сравнения сохранённого authorUid с auth.uid (идентификатором пользователя), переданным в токене аутентификации.

Создавать

Эта практика авторизации начинается с добавления auth.uid из токена авторизации к каждой новой Post в качестве поля authorUid , чтобы обеспечить сравнение в последующих тестах авторизации.

# Create a new post as the current user
mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}
Обновлять

Когда клиент пытается обновить Post , вы можете проверить переданный auth.uid на соответствие сохраненному authorUid .

# Update one of the current user's posts
mutation UpdatePost($id: UUID!, $text: String, $visibility: String) @auth(level:USER) {
  post_update(
    # only update posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
    data: {
      text: $text
      visibility: $visibility
      # insert the current server time for updatedAt
      updatedAt_expr: "request.time"
    }
  )
}
Удалить

Тот же метод используется для авторизации операций удаления.

# Delete one of the current user's posts
mutation DeletePost($id: UUID!) @auth(level: USER) {
  post_delete(
    # only delete posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
  )
}
# Common display information for a post
fragment DisplayPost on Post {
  id, text, createdAt, updatedAt
  author { uid, name }
}
Список
# List all posts belonging to the current user
query ListMyPosts @auth(level: USER) {
  posts(where: {
    userUid: {eq_expr: "auth.uid"}
  }) {
    # See the fragment above
    ...DisplayPost
    # also show visibility since it is user-controlled
    visibility
  }
}
Получать
# Get a post only if it belongs to the current user
query GetMyPost($id: UUID!) @auth(level: USER) {
  post(key: {id: $id},
    first: {where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}}
      }}, {
      # See the fragment above
      ...DisplayPost
      # also show visibility since it is user-controlled
      visibility
  }
}

Фильтр данных

Система авторизации Data Connect позволяет создавать сложные фильтры в сочетании с предустановленными уровнями доступа, такими как PUBLIC а также использовать данные из токенов авторизации.

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

Фильтр по атрибутам ресурса

Здесь авторизация не основана на токенах авторизации, поскольку базовый уровень безопасности установлен на PUBLIC . Однако мы можем явно указать записи в нашей базе данных как подходящие для публичного доступа; предположим, что в нашей базе данных есть записи Post с visibility «public».

# List all posts marked as 'public' visibility
query ListPublicPosts @auth(level: PUBLIC) {
  posts(where: {
    # Test that visibility is "public"
    visibility: {eq: "public"}
    # Only display articles that are already published
    publishedAt: {lt_expr: "request.time"}
  }) {
    # see the fragment above
    ...DisplayPost
  }
}
Фильтр по заявлениям пользователей

Предположим, вы настроили пользовательские утверждения, которые передают токены авторизации для идентификации пользователей тарифного плана «Pro» для вашего приложения, отмеченные полем auth.token.plan в токене авторизации. Ваши выражения могут проверяться по этому полю.

# List all public or pro posts, only permitted if user has "pro" plan claim
query ProListPosts @auth(expr: "auth.token.plan == 'pro'") {
  posts(where: {
    # display both public posts and "pro" posts
    visibility: {in: ['public', 'pro']},
    # only display articles that are already published
    publishedAt: {lt_expr: "request.time"},
  }) {
    # see the fragment above
    ...DisplayPost
    # show visibility so pro users can see which posts are pro\
    visibility
  }
}
Фильтр по заказу + лимиту

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

# Show 2 oldest Pro post as a preview
query ProTeaser @auth(level: USER) {
  posts(
    where: {
      # show only pro posts
      visibility: {eq: "pro"}
      # that have already been published more than 30 days ago
      publishedAt: {lt_time: {now: true, sub: {days: 30}}}
    },
    # order by publish time
    orderBy: [{publishedAt: DESC}],
    # only return two posts
    limit: 2
  ) {
    # See the fragment above
    ...DisplayPost
  }
}
Фильтр по роли

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

# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
  posts { ...DisplayPost }
}

Добавьте директивы @check и @redact для поиска данных авторизации.

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

Используя поиск данных авторизации, вы можете запрашивать роли на основе идентификатора пользователя и использовать выражения CEL для определения авторизованности мутации. Например, вы можете написать мутацию UpdateMovieTitle , которая позволит авторизованному клиенту обновлять названия фильмов.

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

# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["movie", "user"]) {
  movie: Movie! # implies another field: movieId: UUID!
  user: User!
  role: String!
}

Использование в мутациях

В следующем примере реализации мутация UpdateMovieTitle включает поле query для извлечения данных из MoviePermission и следующие директивы для обеспечения безопасности и надежности операции:

  • Директива @transaction , гарантирующая, что все запросы и проверки авторизации будут выполнены или не будут выполнены атомарно.
  • Директива @redact исключает результаты запроса из ответа, что означает, что наша проверка авторизации выполняется на сервере Data Connect но конфиденциальные данные не предоставляются клиенту.
  • Пара директив @check для оценки логики авторизации по результатам запроса, например, проверка того, имеет ли заданный идентификатор пользователя соответствующую роль для внесения изменений.

mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    # Step 1a: Use @check to test if the user has any role associated with the movie
    # Here the `this` binding refers the lookup result, i.e. a MoviePermission object or null
    # The `this != null` expression could be omitted since rejecting on null is default behavior
    ) @check(expr: "this != null", message: "You do not have access to this movie") {
      # Step 1b: Check if the user has the editor role for the movie
      # Next we execute another @check; now `this` refers to the contents of the `role` field
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

Использовать в запросах

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

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

query GetMovieEditors($movieId: UUID!) @auth(level: PUBLIC) {
  moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
    role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
  }
  moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
    user {
      id
      username
    }
  }
}

Антипаттерны, которых следует избегать при авторизации

В предыдущем разделе рассматриваются шаблоны, которым следует следовать при использовании директивы @auth .

Вам также следует знать о важных антипаттернах, которых следует избегать.

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

Firebase Authentication — мощный инструмент для представления потоков аутентификации и безопасного сбора данных аутентификации, таких как зарегистрированные идентификаторы пользователей и многочисленные поля, хранящиеся в токенах аутентификации.

Не рекомендуется передавать идентификаторы пользователей и данные токенов авторизации в аргументах запросов и мутаций.

# Antipattern!
# This incorrectly allows any user to view any other user's posts
query AllMyPosts($userId: String!) @auth(level: USER) {
  posts(where: {authorUid: {eq: $userId}}) {
    id, text, createdAt
  }
}

Избегайте использования уровня доступа USER без каких-либо фильтров.

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

# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
  documents {
    id
    title
    text
  }
}

Избегайте использования уровня доступа PUBLIC или USER для создания прототипов.

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

После завершения первоначального прототипирования таким образом начните переходить от NO_ACCESS к готовой к использованию в производстве авторизации с уровнями PUBLIC и USER . Однако не развертывайте их как PUBLIC или USER без добавления дополнительной логики, как показано в этом руководстве.

# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
  post: post_delete(
    id: $id,
  )
}

Избегайте авторизации на основе непроверенных адресов электронной почты.

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

# Antipattern!
# Anyone can claim an email address during sign-in
mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email.endsWith('@example.com')") {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}

Также проверьте auth.token.email_verified

mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email_veri&&fied  auth.token.email.endsWith('@example.com')") {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}

Аудит авторизации с помощью Firebase CLI

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

Data Connect помогает вам проводить аудит вашей стратегии авторизации, анализируя код коннектора при развертывании на сервере с помощью firebase deploy из Firebase CLI. Вы можете использовать этот аудит для анализа вашей кодовой базы.

При развертывании коннекторов CLI выведет оценки для существующего, измененного и нового кода операций в вашем коннекторе.

Для измененных и новых операций CLI выдает предупреждения и запрашивает подтверждение при использовании определенных уровней доступа в новых операциях или при изменении существующих операций для использования этих уровней доступа.

Предупреждения и подсказки всегда появляются для:

  • PUBLIC

Предупреждения и подсказки возникают на следующих уровнях доступа, если вы не дополняете их фильтрами с использованием auth.uid :

  • USER
  • USER_ANON
  • USER_EMAIL_VERIFIED

Подавите предупреждения о небезопасных операциях с помощью аргумента @auth(insecureReason:)

Во многих случаях вы придете к выводу, что использование уровней доступа PUBLIC и USER* вполне целесообразно.

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

Вы можете отключить предупреждения для таких операций с помощью @auth(insecureReason:) . Например:

query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
  {
    items {
      id name
    }
  }

Используйте Firebase App Check для аттестации приложений

Аутентификация и авторизация — важнейшие компоненты безопасности Data Connect . В сочетании с аттестацией приложений аутентификация и авторизация обеспечивают очень надёжное решение безопасности.

Благодаря аттестации через Firebase App Check устройства, на которых запущено ваше приложение, будут использовать поставщика аттестации приложений или устройств, который подтверждает, что операции Data Connect исходят из вашего подлинного приложения, а запросы — с подлинного, нетронутого устройства. Эта аттестация прикрепляется к каждому запросу, который ваше приложение отправляет в Data Connect .

Чтобы узнать, как включить App Check для Data Connect и включить его клиентский SDK в свое приложение, ознакомьтесь с обзором App Check .

Уровни аутентификации для директивы @auth(level)

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

Уровень Определение
PUBLIC Операцию может выполнить любой человек как с аутентификацией, так и без нее.

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

Эквивалент @auth(expr: "true")

Фильтры и выражения @auth нельзя использовать в сочетании с этим уровнем доступа. Любые такие выражения приведут к ошибке 400 (неправильный запрос).
USER_ANON Любой идентифицированный пользователь, включая тех, кто вошел в систему анонимно с помощью Firebase Authentication , имеет право выполнить запрос или мутацию.

Примечание: USER_ANON является надмножеством USER .

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

Поскольку анонимные потоки входа в Authentication выдают uid , уровень USER_ANON эквивалентен
@auth(expr: "auth.uid != nil")
USER Любой пользователь, вошедший в систему с использованием Firebase Authentication имеет право выполнять запрос или мутацию, за исключением анонимных пользователей, вошедших в систему.

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

Эквивалентно @auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")"
USER_EMAIL_VERIFIED Любой пользователь, вошедший в систему с помощью Firebase Authentication и подтвержденного адреса электронной почты, имеет право выполнить запрос или мутацию.

Внимание: поскольку проверка адреса электронной почты выполняется с помощью Authentication , она основана на более надёжном методе Authentication , поэтому этот уровень обеспечивает дополнительную безопасность по сравнению с USER или USER_ANON . Этот уровень проверяет только, что пользователь вошел в систему с помощью Authentication и подтверждённого адреса электронной почты, и сам по себе не выполняет других проверок, например, на принадлежность данных пользователю. См. примеры передового опыта и альтернативные варианты .

Эквивалентно @auth(expr: "auth.uid != nil && auth.token.email_verified")"
NO_ACCESS Эту операцию невозможно выполнить вне контекста Admin SDK.

Эквивалент @auth(expr: "false")

CEL-ссылка для @auth(expr)

Как показано в примерах в других разделах этого руководства, вы можете и должны использовать выражения, определенные в Common Expression Language (CEL), для управления авторизацией для Data Connect с помощью директив @auth(expr:) и @check .

В этом разделе рассматривается синтаксис CEL, необходимый для создания выражений для этих директив.

Полная справочная информация по CEL приведена в спецификации CEL .

Тестовые переменные, передаваемые в запросах и мутациях

Синтаксис @auth(expr) позволяет получать доступ к переменным из запросов и мутаций и проверять их.

Например, вы можете включить переменную операции, такую ​​как $status , используя vars.status .

mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")

Данные, доступные для выражений: запрос, ответ, это

Вы используете данные для:

  • Оценка с помощью выражений CEL в директивах @auth(expr:) и @check(expr:)
  • Назначение с использованием выражений сервера, <field>_expr .

Оба выражения CEL @auth(expr:) и @check(expr:) могут вычислять следующее:

  • request.operationName
  • vars (псевдоним для request.variables )
  • auth (псевдоним для request.auth )

В мутациях вы можете получить доступ и назначить содержимое:

  • response (для проверки частичных результатов в многошаговой логике)

Кроме того, выражения @check(expr:) могут вычислять:

  • this (значение текущего поля)
  • response (для проверки частичных результатов в многошаговой логике)

Привязка request.operationName

Привязка request.operarationName хранит тип операции: запрос или мутация.

Привязка vars (request.vars)

Связывание vars позволяет вашим выражениям получать доступ ко всем переменным, переданным в вашем запросе или мутации.

Вы можете использовать vars.<variablename> в выражении как псевдоним для полностью квалифицированного request.variables.<variablename> :

# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")

Привязка auth (request.auth)

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

В фильтрах и выражениях вы можете использовать auth как псевдоним для request.auth .

Привязка аутентификации содержит следующую информацию:

  • uid : уникальный идентификатор пользователя, назначенный запрашивающему пользователю.
  • token : Карта значений, собранных при Authentication .

Более подробную информацию о содержимом auth.token см. в разделе Данные в токенах аутентификации.

Привязка response

Привязка response содержит данные, собираемые сервером в ответ на запрос или мутацию по мере сборки этих данных .

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

Привязка response структурирована в соответствии с формой связанной с ней операции, включая (несколько) вложенных полей и (если применимо) встроенные запросы.

Обратите внимание, что при доступе к встроенным данным ответа на запрос поля могут содержать любой тип данных в зависимости от данных, запрошенных во встроенном запросе; при доступе к данным, возвращаемым полями мутации, такими как _insert и _delete , они могут содержать ключи UUID, количество удалений, значения NULL (см. справочник по мутациям ).

Например:

  • В мутации, содержащей встроенный запрос, привязка response содержит данные поиска в response.query.<fieldName>.<fieldName>.... , в данном случае response.query.todoList и response.query.todoList.priority .
mutation CheckTodoPriority(
  $uniqueListName: String!
) {
  # This query is identified as `response.query`
  query @check(expr: "response.query.todoList.priority == 'high'", message: "This list is not for high priority items!") {
    # This field is identified as `response.query.todoList`
    todoList(where: { name: $uniqueListName }) {
      # This field is identified as `response.query.todoList.priority`
      priority
    }
  }
}
  • В многошаговой мутации, например с несколькими полями _insert , привязка response содержит частичные данные в response.<fieldName>.<fieldName>.... , в данном случае response.todoList_insert.id .
mutation CreateTodoListWithFirstItem(
  $listName: String!,
  $itemContent: String!
) @transaction {
  # Step 1
  todoList_insert(data: {
    id_expr: "uuidV4()",
    name: $listName,
  })
  # Step 2:
  todo_insert(data: {
    listId_expr: "response.todoLis<t_insert.id" # -- Grab the newly generated ID from the partial response so far.
    content: $itemContent,
  })
}

this привязка

Привязка this выполняется к полю, к которому привязана директива @check . В простейшем случае вы можете оценивать результаты запроса с одним значением.

mutation UpdateMovieTitle (
  $movieId: UUID!,
  $newTitle: String!)
  @auth(level: USER)
  @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    ) {
      # Check if the user has the editor role for the movie. `this` is the string value of `role`.
      # If the parent moviePermission is null, the @check will also fail automatically.
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

Если возвращаемое поле встречается несколько раз, поскольку любой предок является списком, каждое вхождение проверяется с this к каждому значению.

Для любого заданного пути, если предок равен null или [] , поле не будет достигнуто, и вычисление CEL для этого пути будет пропущено. Другими словами, вычисление выполняется только тогда, когда this равен null или не null , но никогда undefined .

Если поле само является списком или объектом, this следует той же структуре (включая всех потомков, выбранных в случае объектов), как показано в следующем примере.

mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query {
    moviePermissions( # Now we query for a list of all matching MoviePermissions.
      where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
    # This time we execute the @check on the list, so `this` is the list of objects.
    # We can use the `.exists` macro to check if there is at least one matching entry.
    ) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
      role
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

Синтаксис сложного выражения

Вы можете записать более сложные выражения, комбинируя операторы && и || .

mutation UpsertUser($username: String!) @auth(expr: "(auth != n&&ull)  (vars.username == ';joe')")

В следующем разделе описаны все доступные операторы.

Операторы и приоритет операторов

Используйте следующую таблицу в качестве справочного материала по операторам и их приоритету.

Даны произвольные выражения a и b , поле f и индекс i .

Оператор Описание Ассоциативность
a[i] a() af Индекс, вызов, доступ к полю слева направо
!a -a Унарное отрицание справа налево
a/ba%ba*b Мультипликативные операторы слева направо
a+b ab Аддитивные операторы слева направо
a>b a>=b a<b a<=b Реляционные операторы слева направо
a in b Наличие в списке или на карте слева направо
type(a) == t Сравнение типов, где t может быть bool, int, float, number, string, list, map, timestamp или duration слева направо
a==ba!=b Операторы сравнения слева направо
a && b Условное И слева направо
a || b Условное ИЛИ слева направо
a ? true_value : false_value Троичное выражение слева направо

Данные в токенах авторизации

Объект auth.token может содержать следующие значения:

Поле Описание
email Адрес электронной почты, связанный с учетной записью, если он есть.
email_verified true если пользователь подтвердил, что у него есть доступ к адресу email . Некоторые провайдеры автоматически проверяют принадлежащие им адреса электронной почты.
phone_number Номер телефона, связанный с учетной записью, если он есть.
name Отображаемое имя пользователя, если установлено.
sub Идентификатор пользователя Firebase. Он уникален в рамках проекта.
firebase.identities Словарь всех идентификаторов, связанных с учётной записью пользователя. Ключами словаря могут быть любые из следующих: email , phone , google.com , facebook.com , github.com , twitter.com . Значения словаря представляют собой массивы уникальных идентификаторов для каждого поставщика идентификаторов, связанного с учётной записью. Например, auth.token.firebase.identities["google.com"][0] содержит первый идентификатор пользователя Google, связанный с учётной записью.
firebase.sign_in_provider Поставщик услуг входа, используемый для получения этого токена. Может быть одной из следующих строк: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com .
firebase.tenant Идентификатор tenantId, связанный с учётной записью, если он есть. Например, tenant2-m6tyz

Дополнительные поля в токенах JWT ID

Вы также можете получить доступ к следующим полям auth.token :

Пользовательские заявки на токены
alg Алгоритм "RS256"
iss Эмитент Адрес электронной почты учетной записи службы вашего проекта
sub Предмет Адрес электронной почты учетной записи службы вашего проекта
aud Аудитория "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Выдано в момент времени Текущее время в секундах с начала эпохи UNIX
exp Время истечения срока действия Время (в секундах с начала эпохи UNIX), по истечении которого истекает срок действия токена. Оно может быть не более чем на 3600 секунд позже iat .
Примечание: это определяет только время истечения срока действия самого токена . Но после того, как вы введёте пользователя с помощью signInWithCustomToken() , он останется в системе устройства до тех пор, пока его сеанс не будет аннулирован или пользователь не выйдет из системы.
<claims> (необязательно) Необязательные пользовательские утверждения для включения в токен, доступ к которому можно получить через auth.token (или request.auth.token ) в выражениях. Например, если вы создаёте пользовательское утверждение adminClaim , доступ к нему можно получить через auth.token.adminClaim .

Что дальше?