O Firebase Data Connect oferece segurança robusta do lado do cliente com:
- Autorização de clientes da Web e de dispositivos móveis
- Controles de autorização individuais no nível da consulta e da mutação
- Atestado do app com Firebase App Check.
O Data Connect estende essa segurança com:
- Autorização do lado do servidor
- Segurança do usuário do projeto do Firebase e do Cloud SQL com o IAM.
Autorizar consultas e mutações do cliente
O Data Connect é totalmente integrado ao Firebase Authentication. Assim, você pode usar dados avançados sobre os usuários que estão acessando seus dados (autenticação) no design de quais dados esses usuários podem acessar (autorização).
O Data Connect fornece uma diretiva @auth
para consultas e
mutações que permite definir o nível de autenticação necessário para autorizar
a operação. Este guia
apresenta a diretiva @auth
com exemplos.
Além disso, o Data Connect oferece suporte à execução de consultas incorporadas em mutações. Assim, é possível recuperar outros critérios de autorização armazenados no banco de dados e usá-los em diretivas @check
para decidir se as mutações de inclusão estão autorizadas. Para esse caso de autorização, a diretiva @redact
permite controlar se os resultados da consulta são retornados aos clientes no
protocolo de rede e se a consulta incorporada é omitida nos SDKs gerados. Confira a introdução a essas diretivas, com exemplos.
Entender a diretiva @auth
Você pode parametrizar a diretiva @auth
para seguir um dos vários níveis de acesso predefinidos que abrangem muitos cenários comuns. Esses níveis variam de
PUBLIC
(que permite consultas e mutações de todos os clientes sem
autenticação de qualquer tipo) a NO_ACCESS
(que não permite consultas e
mutações fora de ambientes de servidor privilegiados usando o SDK Admin do
Firebase). Cada um desses níveis está correlacionado com fluxos de autenticação fornecidos pelo Firebase Authentication.
Nível | Definição |
---|---|
PUBLIC |
A operação pode ser executada por qualquer pessoa, com ou sem autenticação. |
PUBLIC |
A operação pode ser executada por qualquer pessoa, com ou sem autenticação. |
USER_ANON |
Qualquer usuário identificado, incluindo aqueles que fizeram login anonimamente com Firebase Authentication, está autorizado a realizar a consulta ou mutação. |
USER |
Qualquer usuário que tenha feito login com Firebase Authentication está autorizado a executar a consulta ou mutação, exceto usuários anônimos. |
USER_EMAIL_VERIFIED |
Qualquer usuário que tenha feito login com Firebase Authentication e um endereço de e-mail verificado está autorizado a realizar a consulta ou mutação. |
NO_ACCESS |
Essa operação não pode ser executada fora de um contexto do SDK Admin. |
Usando esses níveis de acesso predefinidos como ponto de partida, é possível definir verificações de autorização complexas e robustas na diretiva @auth
usando filtros where
e expressões da Common Expression Language (CEL) avaliadas no servidor.
Use a diretiva @auth
para implementar cenários comuns de autorização
Os níveis de acesso predefinidos são o ponto de partida para a autorização.
O nível de acesso USER
é o nível básico mais útil para começar.
O acesso totalmente seguro se baseia no nível USER
, além de filtros e expressões que verificam atributos de usuário e de recurso, papéis e outras verificações. Os níveis USER_ANON
e USER_EMAIL_VERIFIED
são variações do caso USER
.
A sintaxe de expressão permite avaliar dados usando um objeto auth
que representa dados de autenticação transmitidos com operações, tanto dados padrão em tokens de autenticação quanto dados personalizados em tokens. Para conferir a lista de campos disponíveis no objeto auth
, consulte a seção de referência.
É claro que há casos de uso em que PUBLIC
é o nível de acesso correto para começar. Um nível de acesso é sempre um ponto de partida, e filtros e expressões adicionais são necessários para uma segurança robusta.
Este guia agora oferece exemplos de como criar em USER
e PUBLIC
.
Um exemplo motivador
Os exemplos de práticas recomendadas a seguir se referem ao seguinte esquema para uma plataforma de blogs com determinado conteúdo bloqueado por um plano de pagamento.
Uma plataforma assim provavelmente modelaria Users
e 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! @def
ault(expr: "request.time")
updatedAt: Timestamp! @default(expr: "request.time")
}
Recursos pertencentes ao usuário
O Firebase recomenda que você crie filtros e expressões que testem a propriedade do usuário de um recurso, nos seguintes casos, a propriedade de Posts
.
Nos exemplos a seguir, os dados dos tokens de autenticação são lidos e comparados usando
expressões. O padrão típico é usar expressões como where: {authorUid:
{eq_expr: "auth.uid"}}
para comparar um authorUid
armazenado com o auth.uid
(ID do usuário) transmitido no token de autenticação.
Criar
Essa prática de autorização começa adicionando o auth.uid
do
token de autenticação a cada novo Post
como um campo authorUid
para permitir a comparação em
testes de autorização subsequentes.
# 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: $visi
bility
})
}
Atualizar
Quando um cliente tenta atualizar um Post
, é possível testar o auth.uid
transmitido
em relação ao authorUid
armazenado.
# 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"
}
)
}
Excluir
A mesma técnica é usada para autorizar operações de exclusão.
# 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 }
}
Lista
# 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
visibil
ity
}
}
Get
# 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
visibil
ity
}
}
Filtrar dados
O sistema de autorização do Data Connect permite escrever filtros sofisticados combinados com níveis de acesso predefinidos, como PUBLIC
, e usar dados de tokens de autenticação.
O sistema de autorização também permite usar apenas expressões, sem um nível de acesso básico, conforme mostrado em alguns dos exemplos a seguir.
Filtrar por atributos de recurso
Aqui, a autorização não é baseada em tokens de autenticação, já que o nível de segurança básico
está definido como PUBLIC
. Mas podemos definir explicitamente os registros no nosso banco de dados como adequados para acesso público. Suponha que temos Post
registros no nosso banco de dados com visibility
definido como "público".
# 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 fr
agment above
...DisplayPost
}
}
Filtrar por declarações do usuário
Aqui, vamos supor que você configurou declarações personalizadas de usuários que são transmitidas em tokens de autenticação para
identificar usuários em um plano "pro" do seu app, sinalizado com um campo auth.token.plan
no token de autenticação. Suas expressões podem ser testadas com base nesse campo.
# 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
}
}
Filtrar por ordem e limite
Ou, talvez, você tenha definido visibility
em registros Post
para identificar que eles são
conteúdo disponível para usuários "pro", mas, para uma prévia ou um teaser de dados,
limite ainda mais o número de registros retornados.
# 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
...DisplayP
ost
}
}
Filtrar por função
Se a declaração personalizada definir um papel admin
, teste e autorize
operações de acordo com isso.
# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
posts { ...Displa
yPost }
}
Adicione as diretivas @check
e @redact
para pesquisar dados de autorização.
Um caso de uso comum de autorização envolve armazenar papéis de autorização personalizados no banco de dados, por exemplo, em uma tabela de permissões especiais, e usar esses papéis para autorizar mutações que criam, atualizam ou excluem dados.
Com as pesquisas de dados de autorização, é possível consultar papéis com base em um userID e usar expressões CEL para decidir se a mutação está autorizada. Por exemplo, você
pode escrever uma mutação UpdateMovieTitle
que permita que um
cliente autorizado atualize títulos de filmes.
Para o restante desta discussão, suponha que o banco de dados do app de resenhas de filmes armazene uma função de autorização em uma tabela 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!
}
Usar em mutações
Na implementação de exemplo a seguir, a mutação UpdateMovieTitle
inclui um campo query
para recuperar dados de MoviePermission
e as
diretivas a seguir para garantir que a operação seja segura e robusta:
- Uma diretiva
@transaction
para garantir que todas as consultas e verificações de autorização sejam concluídas ou falhem atomicamente. - A diretiva
@redact
para omitir os resultados da consulta da resposta. Isso significa que nossa verificação de autorização é realizada no servidor Data Connect, mas os dados sensíveis não são expostos ao cliente. Um par de diretivas
@check
para avaliar a lógica de autorização nos resultados da consulta, como testar se um determinado userID tem uma função adequada para fazer modificações.
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
})
}
Usar em consultas
As pesquisas de dados de autorização também são úteis para restringir consultas com base em papéis ou outras restrições.
No exemplo a seguir, que também usa o esquema MoviePermission
, a consulta verifica se um solicitante tem uma função de "administrador" adequada para ver usuários que podem editar um filme.
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
}
}
}
Antipadrões a serem evitados na autorização
A seção anterior aborda os padrões a serem seguidos ao usar a diretiva @auth
.
Também é importante conhecer os antipadrões importantes para evitar.
Evite transmitir IDs de atributos do usuário e parâmetros de token de autenticação em argumentos de consulta e mutação
O Firebase Authentication é uma ferramenta eficiente para apresentar fluxos de autenticação e capturar dados de autenticação com segurança, como IDs de usuários registrados e vários campos armazenados em tokens de autenticação.
Não é recomendável transmitir IDs de usuário e dados de token de autenticação em argumentos de consulta e mutação.
# 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
}
}
Evite usar o nível de acesso USER
sem filtros
Como discutido várias vezes no guia, os níveis de acesso principais, como USER
, USER_ANON
e USER_EMAIL_VERIFIED
, são bases e pontos de partida para verificações de autorização, que podem ser aprimoradas com filtros e expressões. Usar esses níveis sem um filtro ou uma expressão correspondente que verifique qual usuário está fazendo a solicitação é essencialmente equivalente a usar o nível PUBLIC
.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
documents {
id
title
text
}
}
Evite usar o nível de acesso PUBLIC
ou USER
para prototipagem
Para acelerar o desenvolvimento, pode ser tentador definir todas as operações como o nível de acesso PUBLIC
ou USER
sem mais melhorias para autorizar todas as operações e permitir que você teste seu código rapidamente.
Quando você tiver feito a prototipagem inicial dessa forma, comece a mudar de
NO_ACCESS
para autorização pronta para produção com níveis PUBLIC
e USER
.
No entanto, não os implante como PUBLIC
ou USER
sem adicionar outra lógica, conforme mostrado neste guia.
# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
post: post_delete(
id: $id,
)
}
Evite basear a autorização em endereços de e-mail não verificados
Conceder acesso a usuários em um domínio específico é uma ótima maneira de limitar o acesso. No entanto, qualquer pessoa pode reivindicar a propriedade de um e-mail durante o login. Conceda acesso apenas a endereços de e-mail verificados pelo Firebase Authentication.
# 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
})
}
Confira também 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
})
}
Auditar a autorização com a CLI do Firebase
Como indicado anteriormente, os níveis de acesso predefinidos, como PUBLIC
e USER
, são o ponto de partida para uma autorização robusta e devem ser usados com outras verificações de autorização baseadas em filtro e expressão.
Eles não devem ser usados sozinhos sem considerar cuidadosamente o caso de uso.
O Data Connect ajuda você a auditar sua estratégia de autorização analisando
o código do conector ao implantar no servidor usando firebase deploy
da
CLI Firebase. Use essa auditoria para revisar sua base de código.
Ao implantar os conectores, a CLI vai gerar avaliações para o código de operação existente, modificado e novo no conector.
Para operações modificadas e novas, a CLI emite avisos e solicita confirmação quando você usa determinados níveis de acesso nas novas operações ou quando modifica as operações atuais para usar esses níveis de acesso.
Os avisos e solicitações sempre ocorrem para:
PUBLIC
Além disso, avisos e solicitações aparecem nos seguintes níveis de acesso quando você não os aumenta com filtros usando auth.uid
:
USER
USER_ANON
USER_EMAIL_VERIFIED
Suprimir avisos de operação não segura com o argumento @auth(insecureReason:)
Em muitos casos, você vai concluir que usar os níveis de acesso PUBLIC
e USER*
é perfeitamente adequado.
Quando o conector tem muitas operações, talvez você queira uma saída de auditoria de segurança mais clara e relevante que omita operações que normalmente acionariam um aviso, mas que você sabe que têm o nível de acesso correto.
É possível suprimir avisos para essas operações com @auth(insecureReason:)
.
Exemplo:
query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
{
items {
id name
}
}
Usar Firebase App Check para atestado do app
A autenticação e a autorização são componentes essenciais da segurança de Data Connect. A autenticação e a autorização combinadas com a declaração do app criam uma solução de segurança muito robusta.
Com o atestado pelo Firebase App Check, os dispositivos que executam seu app usam um provedor de atestado de app ou dispositivo que atesta que as operações Data Connect se originam do seu app autêntico e que as solicitações vêm de um dispositivo autêntico que não foi adulterado. Esse atestado é anexado a todas as solicitações que o app faz ao Data Connect.
Para saber como ativar o App Check para Data Connect e incluir o SDK do cliente dele no app, confira a visão geral do App Check.
Níveis de autenticação para a diretiva @auth(level)
A tabela a seguir lista todos os níveis de acesso padrão e os equivalentes em CEL. Os níveis de autenticação são listados de forma ampla a restrita. Cada nível abrange todos os usuários que correspondem aos níveis a seguir.
Nível | Definição |
---|---|
PUBLIC |
A operação pode ser executada por qualquer pessoa, com ou sem autenticação.
Considerações:os dados podem ser lidos ou modificados por qualquer usuário. O Firebase recomenda esse nível de autorização para dados acessíveis publicamente, como listas de produtos ou mídia. Confira os exemplos e alternativas de práticas recomendadas. Equivalente a @auth(expr: "true")
Não é possível usar filtros e expressões @auth em combinação
com esse nível de acesso. Essas expressões vão falhar com um erro 400 de solicitação
incorreta.
|
USER_ANON |
Qualquer usuário identificado, incluindo aqueles que fizeram login anonimamente
com Firebase Authentication, está autorizado a realizar a consulta ou mutação.
Observação: USER_ANON é um superconjunto de USER .
Considerações:projete com cuidado suas consultas e mutações para esse nível de autorização. Esse nível permite que o usuário faça login anonimamente (login automático vinculado apenas a um dispositivo do usuário) com Authentication e não realiza outras verificações por conta própria, por exemplo, se os dados pertencem ao usuário. Confira os exemplos e alternativas de práticas recomendadas. Como os fluxos de login anônimo Authentication emitem um uid , o nível
USER_ANON é equivalente a
@auth(expr: "auth.uid != nil")
|
USER |
Qualquer usuário que tenha feito login com Firebase Authentication está autorizado a
executar a consulta ou mutação, exceto usuários anônimos.
Considerações:projete com cuidado suas consultas e mutações para esse nível de autorização. Esse nível verifica apenas se o usuário fez login com Authentication e não realiza outras verificações por conta própria, por exemplo, se os dados pertencem ao usuário. Confira os exemplos e alternativas de práticas recomendadas. Equivalente a @auth(expr: "auth.uid != nil &&
auth.token.firebase.sign_in_provider != 'anonymous'")"
|
USER_EMAIL_VERIFIED |
Qualquer usuário que tenha feito login com Firebase Authentication e um endereço de e-mail verificado está autorizado a realizar a consulta ou mutação.
Considerações:como a verificação de e-mail é realizada usando Authentication, ela se baseia em um método Authentication mais robusto. Assim, esse nível oferece mais segurança em comparação com USER ou
USER_ANON . Esse nível só verifica se o usuário fez login com Authentication e um e-mail verificado. Ele não realiza outras verificações, por exemplo, se os dados pertencem ao usuário. Confira os exemplos e alternativas de práticas recomendadas.
Equivalente a @auth(expr: "auth.uid != nil &&
auth.token.email_verified")" |
NO_ACCESS |
Essa operação não pode ser executada fora de um contexto do SDK Admin.
Equivalente a @auth(expr: "false") |
Referência de CEL para @auth(expr)
Como mostrado em exemplos em outras partes deste guia, você pode e deve usar expressões definidas na Common Expression Language (CEL) para controlar a autorização do Data Connect usando as diretivas @auth(expr:)
e @check
.
Esta seção aborda a sintaxe da CEL relevante para criar expressões para essas diretivas.
As informações de referência completas da CEL estão disponíveis na especificação da CEL.
Testar variáveis transmitidas em consultas e mutações
A sintaxe @auth(expr)
permite acessar e testar variáveis de consultas e mutações.
Por exemplo, é possível incluir uma variável de operação, como $status
, usando
vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.statu
s)")
Dados disponíveis para expressões: request, response, this
Você usa dados para:
- Avaliação com expressões CEL nas diretivas
@auth(expr:)
e@check(expr:)
- Atribuição usando expressões de servidor,
<field>_expr
.
As expressões CEL @auth(expr:)
e @check(expr:)
podem avaliar o seguinte:
request.operationName
vars
(alias derequest.variables
)auth
(alias derequest.auth
)
Nas mutações, é possível acessar e atribuir o conteúdo de:
response
(para verificar resultados parciais em uma lógica de várias etapas)
Além disso, as expressões @check(expr:)
podem avaliar:
this
(o valor do campo atual)response
(para verificar resultados parciais em uma lógica de várias etapas)
A vinculação request.operationName
A vinculação request.operarationName
armazena o tipo de operação, consulta ou mutação.
A vinculação vars
(request.vars)
A vinculação vars
permite que suas expressões acessem todas as variáveis
transmitidas na consulta ou mutação.
Você pode usar vars.<variablename>
em uma expressão como um alias para o
request.variables.<variablename>
totalmente qualificado:
# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.va
riables.v == 'hello'")
A vinculação auth
(request.auth)
O Authentication identifica os usuários que solicitam acesso aos seus dados e fornece essas informações como uma vinculação que pode ser usada nas suas expressões.
Nos filtros e expressões, é possível usar auth
como um alias para request.auth
.
A vinculação de autenticação contém as seguintes informações:
uid
: um ID de usuário exclusivo, atribuído ao usuário solicitante.token
: um mapa de valores coletados por Authentication.
Para mais detalhes sobre o conteúdo de auth.token
, consulte
Dados em tokens de autenticação
A vinculação response
A vinculação response
contém os dados que estão sendo montados pelo servidor em
resposta a uma consulta ou mutação à medida que esses dados são montados.
À medida que a operação avança e cada etapa é concluída com sucesso, response
contém dados de resposta das etapas concluídas.
A vinculação response
é estruturada de acordo com a forma da operação associada, incluindo campos aninhados (múltiplos) e consultas incorporadas (se aplicável).
Ao acessar dados de resposta de consulta incorporada, os campos podem conter qualquer tipo de dado, dependendo dos dados solicitados na consulta incorporada. Ao acessar dados retornados por campos de mutação, como _insert
s e _delete
s, eles podem conter chaves UUID, número de exclusões, nulos. Consulte a referência de mutações.
Exemplo:
- Em uma mutação que contém uma consulta incorporada, a vinculação
response
contém dados de pesquisa emresponse.query.<fieldName>.<fieldName>....
, neste caso,response.query.todoList
eresponse.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
}
}
}
- Em uma mutação de várias etapas, por exemplo, com vários campos
_insert
, a vinculaçãoresponse
contém dados parciais emresponse.<fieldName>.<fieldName>....
, neste caso,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,
})
}
A vinculação this
A vinculação this
é avaliada como o campo a que a diretiva @check
está anexada. Em um caso básico, você pode avaliar resultados de consultas de valor único.
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
})
}
Se o campo retornado ocorrer várias vezes porque um ancestral é uma lista, cada
ocorrência será testada com this
vinculada a cada valor.
Para qualquer caminho, se um ancestral for null
ou []
, o campo não será alcançado e a avaliação do CEL será ignorada para esse caminho. Em outras palavras, a avaliação só acontece quando this
é null
ou não null
, mas nunca undefined
.
Quando o campo é uma lista ou um objeto, this
segue a mesma estrutura (incluindo todos os descendentes selecionados no caso de objetos), conforme ilustrado no exemplo a seguir.
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
})
}
Sintaxe de expressão complexa
É possível escrever expressões mais complexas combinando com os operadores &&
e ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != n&&ull) (vars.username == '
;joe')")
A seção a seguir descreve todos os operadores disponíveis.
Operadores e precedência do operador
Use a tabela a seguir como referência para os operadores e a precedência correspondente deles.
Expressões arbitrárias fornecidas a
e b
, um campo f
e um índice i
.
Operador | Descrição | Associatividade |
---|---|---|
a[i] a() a.f |
Índice, chamada, acesso ao campo | da esquerda para a direita |
!a -a |
Negação unária | da direita para a esquerda |
a/b a%b a*b |
Operadores multiplicativos | da esquerda para a direita |
a+b a-b |
Operadores aditivos | da esquerda para a direita |
a>b a>=b a<b a<=b |
Operadores relacionais | da esquerda para a direita |
a in b |
Existência na lista ou no mapa | da esquerda para a direita |
type(a) == t |
Comparação de tipos, em que t pode ser bool, int, float,
number, string, list, map, timestamp ou duration |
da esquerda para a direita |
a==b a!=b |
Operadores de comparação | da esquerda para a direita |
a && b |
Condicional E | da esquerda para a direita |
a || b |
Condicional OU | da esquerda para a direita |
a ? true_value : false_value |
Expressão ternária | da esquerda para a direita |
Dados em tokens de autenticação
O objeto auth.token
pode conter os seguintes valores:
Campo | Descrição |
---|---|
email |
O endereço de e-mail associado à conta, se essa informação existir. |
email_verified |
true se o usuário tiver verificado que tem acesso ao endereço email . Alguns provedores verificam automaticamente esses endereços de e-mail. |
phone_number |
O número de telefone associado à conta, se essa informação existir. |
name |
O nome de exibição do usuário, se ele tiver sido definido. |
sub |
O UID do Firebase do usuário. Ele é exclusivo dentro de um projeto. |
firebase.identities |
O dicionário de todas as identidades associadas à conta desse usuário. As chaves do dicionário podem ser qualquer uma das seguintes: email , phone , google.com , facebook.com , github.com , twitter.com . Os valores do dicionário são matrizes de identificadores exclusivos de cada provedor de identidade associado à conta. Por exemplo, auth.token.firebase.identities["google.com"][0] contém o primeiro ID de usuário do Google associado à conta. |
firebase.sign_in_provider |
O provedor de entrada usado para receber esse token. Pode ser uma das seguintes strings: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant |
O tenantId associado à conta, se houver. Por exemplo, tenant2-m6tyz . |
Campos adicionais em tokens de ID JWT
Você também pode acessar os seguintes campos auth.token
:
Declarações de tokens personalizados | ||
---|---|---|
alg |
Algoritmo | "RS256" |
iss |
Emissor | Endereço de e-mail da conta de serviço do seu projeto |
sub |
Assunto | Endereço de e-mail da conta de serviço do seu projeto |
aud |
Público | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Hora de emissão | A hora atual, em segundos, desde a época do UNIX |
exp |
Tempo de expiração |
O tempo, em segundos, desde a época do UNIX, em que o token expira. Pode ser no máximo 3.600 segundos depois de iat .
Observação: ele controla o tempo apenas quando o token personalizado expira. No entanto, quando você faz o login de um usuário utilizando signInWithCustomToken() , ele permanece conectado ao
dispositivo até que a sessão seja invalidada ou que o usuário se desconecte.
|
<claims> (opcional) |
Declarações personalizadas opcionais a serem incluídas no token, que podem ser acessadas por
auth.token (ou request.auth.token ) em
expressões. Por exemplo, se você criar uma declaração personalizada
adminClaim , poderá acessá-la com
auth.token.adminClaim .
|
A seguir
- O Firebase Data Connect fornece um SDK Admin para que você possa realizar consultas e mutações em ambientes privilegiados.
- Saiba mais sobre a segurança do IAM no guia de gerenciamento de serviços e bancos de dados.