Firebase Data Connect offre une sécurité côté client robuste grâce aux éléments suivants :
- Autorisation des clients mobiles et Web
- Contrôles d'autorisation individuels au niveau des requêtes et des mutations
- Attestation d'application avec Firebase App Check.
Data Connect étend cette sécurité avec :
- Autorisation côté serveur
- Sécurité des projets Firebase et des utilisateurs Cloud SQL avec IAM.
Autoriser les requêtes et mutations client
Data Connect est entièrement intégré à Firebase Authentication. Vous pouvez donc utiliser des données détaillées sur les utilisateurs qui accèdent à vos données (authentification) dans votre conception pour déterminer les données auxquelles ces utilisateurs peuvent accéder (autorisation).
Data Connect fournit une directive @auth
pour les requêtes et les mutations qui vous permet de définir le niveau d'authentification requis pour autoriser l'opération. Ce guide présente l'instruction @auth
avec des exemples.
De plus, Data Connect permet d'exécuter des requêtes intégrées dans des mutations. Vous pouvez ainsi récupérer des critères d'autorisation supplémentaires que vous avez stockés dans votre base de données et les utiliser dans des directives @check
pour déterminer si les mutations englobantes sont autorisées. Dans ce cas d'autorisation, la directive @redact
vous permet de contrôler si les résultats de la requête sont renvoyés aux clients dans le protocole réseau et si la requête intégrée est omise dans les SDK générés. Consultez l'introduction à ces directives, avec des exemples.
Comprendre la directive @auth
Vous pouvez paramétrer la directive @auth
pour suivre l'un des nombreux niveaux d'accès prédéfinis qui couvrent de nombreux scénarios d'accès courants. Ces niveaux vont de PUBLIC
(qui autorise les requêtes et les mutations de tous les clients sans aucune authentification) à NO_ACCESS
(qui interdit les requêtes et les mutations en dehors des environnements de serveur privilégiés utilisant le SDK Firebase Admin). Chacun de ces niveaux est corrélé aux flux d'authentification fournis par Firebase Authentication.
Niveau | Définition |
---|---|
PUBLIC |
L'opération peut être exécutée par n'importe qui, avec ou sans authentification. |
PUBLIC |
L'opération peut être exécutée par n'importe qui, avec ou sans authentification. |
USER_ANON |
Tout utilisateur identifié, y compris ceux qui se sont connectés de manière anonyme avec Firebase Authentication, est autorisé à effectuer la requête ou la mutation. |
USER |
Tout utilisateur connecté avec Firebase Authentication est autorisé à effectuer la requête ou la mutation, à l'exception des utilisateurs connectés de manière anonyme. |
USER_EMAIL_VERIFIED |
Tout utilisateur connecté avec Firebase Authentication et une adresse e-mail validée est autorisé à exécuter la requête ou la mutation. |
NO_ACCESS |
Cette opération ne peut pas être exécutée en dehors d'un contexte SDK Admin. |
En utilisant ces niveaux d'accès prédéfinis comme point de départ, vous pouvez définir des vérifications d'autorisation complexes et robustes dans la directive @auth
à l'aide de filtres where
et d'expressions CEL (Common Expression Language) évaluées sur le serveur.
Utiliser la directive @auth
pour implémenter des scénarios d'autorisation courants
Les niveaux d'accès prédéfinis sont le point de départ pour l'autorisation.
Le niveau d'accès USER
est le niveau de base le plus utile pour commencer.
L'accès entièrement sécurisé s'appuie sur le niveau USER
, ainsi que sur des filtres et des expressions qui vérifient les attributs utilisateur, les attributs de ressources, les rôles et d'autres éléments. Les niveaux USER_ANON
et USER_EMAIL_VERIFIED
sont des variantes du cas USER
.
La syntaxe des expressions vous permet d'évaluer les données à l'aide d'un objet auth
représentant les données d'authentification transmises avec les opérations, à la fois les données standards des jetons d'authentification et les données personnalisées des jetons. Pour obtenir la liste des champs disponibles dans l'objet auth
, consultez la section de référence.
Bien sûr, il existe des cas d'utilisation pour lesquels PUBLIC
est le bon niveau d'accès pour commencer. Encore une fois, un niveau d'accès est toujours un point de départ. Des filtres et des expressions supplémentaires sont nécessaires pour une sécurité renforcée.
Ce guide fournit désormais des exemples de création sur USER
et PUBLIC
.
Exemple motivant
Les exemples de bonnes pratiques suivants font référence au schéma suivant pour une plate-forme de blogs dont certains contenus sont verrouillés derrière un forfait payant.
Une telle plate-forme modéliserait probablement Users
et 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")
}
Ressources appartenant à l'utilisateur
Firebase vous recommande d'écrire des filtres et des expressions qui testent la propriété d'une ressource par l'utilisateur, dans les cas suivants, la propriété de Posts
.
Dans les exemples suivants, les données des jetons d'authentification sont lues et comparées à l'aide d'expressions. Le modèle typique consiste à utiliser des expressions telles que where: {authorUid:
{eq_expr: "auth.uid"}}
pour comparer un authorUid
stocké au auth.uid
(ID utilisateur) transmis dans le jeton d'authentification.
Créer
Cette pratique d'autorisation commence par l'ajout de auth.uid
à partir du jeton d'authentification à chaque nouveau Post
en tant que champ authorUid
pour permettre la comparaison dans les tests d'autorisation de sous-séquence.
# 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
})
}
Mettre à jour
Lorsqu'un client tente de mettre à jour un Post
, vous pouvez tester le auth.uid
transmis par rapport au authorUid
stocké.
# 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"
}
)
}
Supprimer
La même technique est utilisée pour autoriser les opérations de suppression.
# 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 }
}
Liste
# 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
# 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
}
}
Filtrer les données
Le système d'autorisation de Data Connect vous permet d'écrire des filtres sophistiqués combinés à des niveaux d'accès prédéfinis tels que PUBLIC
, ainsi qu'en utilisant les données des jetons d'authentification.
Le système d'autorisation vous permet également d'utiliser des expressions uniquement, sans niveau d'accès de base, comme illustré dans certains des exemples suivants.
Filtrer par attributs de ressource
Ici, l'autorisation n'est pas basée sur les jetons d'authentification, car le niveau de sécurité de base est défini sur PUBLIC
. Toutefois, nous pouvons définir explicitement des enregistrements dans notre base de données comme étant adaptés à l'accès public. Supposons que nous ayons Post
enregistrements dans notre base de données avec visibility
défini sur "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
}
}
Filtrer par revendications utilisateur
Ici, supposons que vous avez configuré des revendications utilisateur personnalisées qui sont transmises dans les jetons d'authentification pour identifier les utilisateurs d'un forfait "Pro" pour votre application, signalés par un champ auth.token.plan
dans le jeton d'authentification. Vos expressions peuvent être testées par rapport à ce champ.
# 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
}
}
Filtrer par ordre et limite
Vous pouvez également définir visibility
dans les enregistrements Post
pour identifier le contenu disponible pour les utilisateurs "Pro", mais pour un aperçu ou une bande-annonce de données, limitez davantage le nombre d'enregistrements renvoyés.
# 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
}
}
Filtrer par rôle
Si votre revendication personnalisée définit un rôle admin
, vous pouvez tester et autoriser les opérations en conséquence.
# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
posts { ...DisplayPost }
}
Ajoutez les directives @check
et @redact
pour rechercher les données d'autorisation.
Un cas d'utilisation courant de l'autorisation consiste à stocker des rôles d'autorisation personnalisés dans votre base de données, par exemple dans une table d'autorisations spéciales, et à utiliser ces rôles pour autoriser les mutations permettant de créer, de mettre à jour ou de supprimer des données.
En utilisant des recherches de données d'autorisation, vous pouvez interroger les rôles en fonction d'un ID utilisateur et utiliser des expressions CEL pour déterminer si la mutation est autorisée. Par exemple, vous pouvez écrire une mutation UpdateMovieTitle
qui permet à un client autorisé de modifier les titres de films.
Pour le reste de cette discussion, supposons que la base de données de l'application d'avis sur les films stocke un rôle d'autorisation dans une table 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!
}
Utilisation dans les mutations
Dans l'exemple d'implémentation suivant, la mutation UpdateMovieTitle
inclut un champ query
pour récupérer les données de MoviePermission
, ainsi que les directives suivantes pour garantir la sécurité et la robustesse de l'opération :
- Directive
@transaction
pour s'assurer que toutes les requêtes et vérifications d'autorisation sont effectuées ou échouent de manière atomique. - La directive
@redact
permet d'omettre les résultats de la requête dans la réponse. Cela signifie que notre vérification de l'autorisation est effectuée sur le serveur Data Connect, mais que les données sensibles ne sont pas exposées au client. Paire de directives
@check
permettant d'évaluer la logique d'autorisation sur les résultats de requête, par exemple pour vérifier qu'un ID utilisateur donné dispose du rôle approprié pour effectuer des modifications.
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
})
}
Utiliser dans les requêtes
Les recherches de données d'autorisation sont également utiles pour restreindre les requêtes en fonction des rôles ou d'autres restrictions.
Dans l'exemple suivant, qui utilise également le schéma MoviePermission
, la requête vérifie si un demandeur dispose d'un rôle "administrateur" approprié pour afficher les utilisateurs qui peuvent modifier un film.
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
}
}
}
Antimodèles à éviter dans l'autorisation
La section précédente décrit les modèles à suivre lorsque vous utilisez la directive @auth
.
Vous devez également connaître les principaux antipatterns à éviter.
Évitez de transmettre les ID d'attributs utilisateur et les paramètres de jeton d'authentification dans les arguments de requête et de mutation.
Firebase Authentication est un outil puissant pour présenter les flux d'authentification et capturer de manière sécurisée les données d'authentification telles que les ID utilisateur enregistrés et de nombreux champs stockés dans les jetons d'authentification.
Il est déconseillé de transmettre des ID utilisateur et des données de jeton d'authentification dans les arguments de requête et de mutation.
# 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
}
}
Évitez d'utiliser le niveau d'accès USER
sans aucun filtre.
Comme indiqué à plusieurs reprises dans le guide, les niveaux d'accès principaux tels que USER
, USER_ANON
et USER_EMAIL_VERIFIED
sont des points de départ et des références pour les vérifications d'autorisation, qui doivent être améliorés avec des filtres et des expressions. L'utilisation de ces niveaux sans filtre ni expression correspondants qui vérifient quel utilisateur effectue la requête équivaut essentiellement à utiliser le niveau PUBLIC
.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
documents {
id
title
text
}
}
Évitez d'utiliser le niveau d'accès PUBLIC
ou USER
pour le prototypage
Pour accélérer le développement, il peut être tentant de définir toutes les opérations sur le niveau d'accès PUBLIC
ou USER
sans autre amélioration pour autoriser toutes les opérations et vous permettre de tester rapidement votre code.
Une fois que vous avez effectué le prototypage initial de cette manière, commencez à passer de NO_ACCESS
à une autorisation prête pour la production avec les niveaux PUBLIC
et USER
.
Toutefois, ne les déployez pas en tant que PUBLIC
ou USER
sans ajouter de logique supplémentaire, comme indiqué dans ce guide.
# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
post: post_delete(
id: $id,
)
}
Éviter de baser l'autorisation sur des adresses e-mail non validées
Accorder l'accès aux utilisateurs d'un domaine spécifique est un excellent moyen de limiter l'accès. Toutefois, n'importe qui peut affirmer être le propriétaire d'une adresse e-mail lors de la connexion. Assurez-vous de n'accorder l'accès qu'aux adresses e-mail validées par 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
})
}
Consultez également auth.token.email_verified
mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email_verified && 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
})
}
Auditer l'autorisation avec la CLI Firebase
Comme indiqué précédemment, les niveaux d'accès prédéfinis tels que PUBLIC
et USER
sont le point de départ d'une autorisation robuste. Ils doivent être utilisés avec des vérifications d'autorisation supplémentaires basées sur des filtres et des expressions.
Elles ne doivent pas être utilisées seules sans avoir soigneusement examiné le cas d'utilisation.
Data Connect vous aide à auditer votre stratégie d'autorisation en analysant le code de votre connecteur lorsque vous le déployez sur le serveur à l'aide de firebase deploy
à partir de l'interface de ligne de commande Firebase. Vous pouvez utiliser cet audit pour vous aider à examiner votre code.
Lorsque vous déployez vos connecteurs, l'interface CLI génère des évaluations pour le code d'opération existant, modifié et nouveau dans votre connecteur.
Pour les opérations modifiées et nouvelles, la CLI émet des avertissements et vous invite à confirmer lorsque vous utilisez certains niveaux d'accès dans vos nouvelles opérations ou lorsque vous modifiez des opérations existantes pour utiliser ces niveaux d'accès.
Des avertissements et des invites s'affichent toujours pour les cas suivants :
PUBLIC
De plus, des avertissements et des invites s'affichent pour les niveaux d'accès suivants lorsque vous ne les augmentez pas avec des filtres utilisant auth.uid
:
USER
USER_ANON
USER_EMAIL_VERIFIED
Supprimer les avertissements d'opération non sécurisée avec l'argument @auth(insecureReason:)
Dans de nombreux cas, vous conclurez que l'utilisation des niveaux d'accès PUBLIC
et USER*
est parfaitement appropriée.
Lorsque votre connecteur contient de nombreuses opérations, vous pouvez souhaiter obtenir des résultats d'audit de sécurité plus clairs et plus pertinents, qui omettent les opérations qui déclencheraient normalement un avertissement, mais dont vous savez qu'elles disposent du bon niveau d'accès.
Vous pouvez supprimer les avertissements pour de telles opérations avec @auth(insecureReason:)
.
Exemple :
query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
{
items {
id name
}
}
Utiliser Firebase App Check pour l'attestation d'application
L'authentification et l'autorisation sont des composants essentiels de la sécurité Data Connect. L'authentification et l'autorisation combinées à l'attestation d'application constituent une solution de sécurité très robuste.
Avec l'attestation via Firebase App Check, les appareils exécutant votre application utiliseront un fournisseur d'attestation d'application ou d'appareil qui atteste que les opérations Data Connect proviennent de votre application authentique et que les requêtes proviennent d'un appareil authentique et non falsifié. Cette attestation est associée à chaque requête que votre application envoie à Data Connect.
Pour savoir comment activer App Check pour Data Connect et inclure son SDK client dans votre application, consultez la présentation de App Check.
Niveaux d'authentification pour la directive @auth(level)
Le tableau suivant répertorie tous les niveaux d'accès standards et leurs équivalents CEL. Les niveaux d'authentification sont listés du plus large au plus précis. Chaque niveau englobe tous les utilisateurs qui correspondent aux niveaux suivants.
Niveau | Définition |
---|---|
PUBLIC |
L'opération peut être exécutée par n'importe qui, avec ou sans authentification.
Points à prendre en compte : les données peuvent être lues ou modifiées par n'importe quel utilisateur. Firebase recommande ce niveau d'autorisation pour les données accessibles au public, comme les fiches produit ou les listes de contenus multimédias. Consultez les exemples de bonnes pratiques et les alternatives. Équivalent à @auth(expr: "true")
Les filtres et expressions @auth ne peuvent pas être utilisés en combinaison
avec ce niveau d'accès. Ces expressions échoueront et renverront une erreur 400 Bad Request.
|
USER_ANON |
Tout utilisateur identifié, y compris ceux qui se sont connectés de manière anonyme avec Firebase Authentication, est autorisé à effectuer la requête ou la mutation.
Remarque : USER_ANON est un sur-ensemble de USER .
Points à prendre en compte : Notez que vous devez concevoir soigneusement vos requêtes et mutations pour ce niveau d'autorisation. Ce niveau permet à l'utilisateur d'être connecté de manière anonyme (connexion automatique liée uniquement à un appareil utilisateur) avec Authentication, et n'effectue pas en soi d'autres vérifications, par exemple pour savoir si les données appartiennent à l'utilisateur. Consultez les exemples de bonnes pratiques et les alternatives. Étant donné que les flux de connexion anonymes Authentication émettent un uid , le niveau USER_ANON équivaut à @auth(expr: "auth.uid != nil")
|
USER |
Tout utilisateur connecté avec Firebase Authentication est autorisé à effectuer la requête ou la mutation, à l'exception des utilisateurs connectés de manière anonyme.
Points à prendre en compte : Notez que vous devez concevoir soigneusement vos requêtes et mutations pour ce niveau d'autorisation. Ce niveau vérifie uniquement que l'utilisateur est connecté avec Authentication et n'effectue pas d'autres vérifications, par exemple pour savoir si les données appartiennent à l'utilisateur. Consultez les exemples de bonnes pratiques et les alternatives. Équivalent à @auth(expr: "auth.uid != nil &&
auth.token.firebase.sign_in_provider != 'anonymous'")"
|
USER_EMAIL_VERIFIED |
Tout utilisateur connecté avec Firebase Authentication et une adresse e-mail validée est autorisé à exécuter la requête ou la mutation.
Points à prendre en compte : La validation de l'adresse e-mail étant effectuée à l'aide de Authentication, elle repose sur une méthode Authentication plus robuste. Ce niveau offre donc une sécurité supplémentaire par rapport à USER ou USER_ANON . Ce niveau vérifie uniquement que l'utilisateur est connecté à Authentication avec une adresse e-mail validée. Il n'effectue pas d'autres vérifications, par exemple pour savoir si les données appartiennent à l'utilisateur. Consultez les exemples de bonnes pratiques et les alternatives.
Équivalent à @auth(expr: "auth.uid != nil &&
auth.token.email_verified")" |
NO_ACCESS |
Cette opération ne peut pas être exécutée en dehors d'un contexte SDK Admin.
Équivalent à @auth(expr: "false") |
Documentation de référence sur CEL pour @auth(expr)
Comme le montrent les exemples ailleurs dans ce guide, vous pouvez et devez utiliser des expressions définies dans le langage CEL (Common Expression Language) pour contrôler l'autorisation pour Data Connect à l'aide des directives @auth(expr:)
et @check
.
Cette section aborde la syntaxe CEL permettant de créer des expressions pour ces directives.
Vous trouverez des informations de référence complètes sur CEL dans la spécification CEL.
Tester les variables transmises dans les requêtes et les mutations
La syntaxe @auth(expr)
vous permet d'accéder aux variables des requêtes et des mutations, et de les tester.
Par exemple, vous pouvez inclure une variable d'opération, telle que $status
, à l'aide de vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")
Données disponibles pour les expressions : request, response, this
Vous utilisez les données pour :
- Évaluation avec des expressions CEL dans les directives
@auth(expr:)
et@check(expr:)
- Attribution à l'aide d'expressions de serveur,
<field>_expr
.
Les expressions CEL @auth(expr:)
et @check(expr:)
peuvent évaluer les éléments suivants :
request.operationName
vars
(alias derequest.variables
)auth
(alias derequest.auth
)
Dans les mutations, vous pouvez accéder au contenu des éléments suivants et l'attribuer :
response
(pour vérifier les résultats partiels dans une logique en plusieurs étapes)
De plus, les expressions @check(expr:)
peuvent évaluer :
this
(valeur du champ actuel)response
(pour vérifier les résultats partiels dans une logique en plusieurs étapes)
Liaison request.operationName
La liaison request.operarationName
stocke le type d'opération, qu'il s'agisse d'une requête ou d'une mutation.
L'association vars
(request.vars)
La liaison vars
permet à vos expressions d'accéder à toutes les variables transmises dans votre requête ou mutation.
Vous pouvez utiliser vars.<variablename>
dans une expression comme alias pour request.variables.<variablename>
complet :
# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")
Liaison auth
(request.auth)
Authentication identifie les utilisateurs qui demandent l'accès à vos données et fournit ces informations sous la forme d'une liaison sur laquelle vous pouvez vous appuyer dans vos expressions.
Dans vos filtres et expressions, vous pouvez utiliser auth
comme alias pour request.auth
.
La liaison d'authentification contient les informations suivantes :
uid
: ID utilisateur unique attribué à l'utilisateur qui effectue la requête.token
: carte des valeurs collectées par Authentication.
Pour en savoir plus sur le contenu de auth.token
, consultez Données dans les jetons d'authentification.
Liaison response
La liaison response
contient les données assemblées par le serveur en réponse à une requête ou à une mutation au fur et à mesure de leur assemblage.
Au fur et à mesure de l'opération, à chaque étape terminée avec succès, response
contient les données de réponse des étapes terminées avec succès.
La liaison response
est structurée en fonction de la forme de son opération associée, y compris les champs imbriqués (multiples) et les requêtes intégrées (le cas échéant).
Notez que lorsque vous accédez aux données de réponse aux requêtes intégrées, les champs peuvent contenir n'importe quel type de données, en fonction des données demandées dans la requête intégrée. Lorsque vous accédez aux données renvoyées par les champs de mutation tels que _insert
et _delete
, ils peuvent contenir des clés UUID, le nombre de suppressions ou des valeurs nulles (consultez la documentation sur les mutations).
Exemple :
- Dans une mutation contenant une requête intégrée, la liaison
response
contient des données de recherche àresponse.query.<fieldName>.<fieldName>....
, dans ce cas,response.query.todoList
etresponse.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
}
}
}
- Dans une mutation en plusieurs étapes, par exemple avec plusieurs champs
_insert
, la liaisonresponse
contient des données partielles àresponse.<fieldName>.<fieldName>....
, dans ce cas,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.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
content: $itemContent,
})
}
Liaison this
La liaison this
est évaluée par rapport au champ auquel la directive @check
est associée. Dans un cas de base, vous pouvez évaluer les résultats de requêtes à valeur unique.
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
})
}
Si le champ renvoyé se produit plusieurs fois parce qu'un ancêtre est une liste, chaque occurrence est testée avec this
lié à chaque valeur.
Pour n'importe quel chemin d'accès, si un ancêtre est null
ou []
, le champ ne sera pas atteint et l'évaluation CEL sera ignorée pour ce chemin d'accès. En d'autres termes, l'évaluation n'a lieu que lorsque this
est null
ou non null
, mais jamais undefined
.
Lorsque le champ lui-même est une liste ou un objet, this
suit la même structure (y compris tous les descendants sélectionnés en cas d'objets), comme illustré dans l'exemple suivant.
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
})
}
Syntaxe des expressions complexes
Vous pouvez écrire des expressions plus complexes en les combinant avec les opérateurs &&
et ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")
La section suivante décrit tous les opérateurs disponibles.
Opérateurs et priorité des opérateurs
Utilisez le tableau suivant comme référence pour les opérateurs et leur priorité correspondante.
Étant donné des expressions arbitraires a
et b
, un champ f
et un index i
.
Opérateur | Description | Associativité |
---|---|---|
a[i] a() a.f |
Index, appel, accès aux champs | de gauche à droite. |
!a -a |
Négation unaire | de droite à gauche |
a/b a%b a*b |
Opérateurs multiplicatifs | de gauche à droite. |
a+b a-b |
Opérateurs additifs | de gauche à droite. |
a>b a>=b a<b a<=b |
Opérateurs relationnels | de gauche à droite. |
a in b |
Présence dans une liste ou sur une carte | de gauche à droite. |
type(a) == t |
Comparaison de types, où t peut être bool, int, float, number, string, list, map, timestamp ou duration |
de gauche à droite. |
a==b a!=b |
Opérateurs de comparaison | de gauche à droite. |
a && b |
AND conditionnel | de gauche à droite. |
a || b |
OR conditionnel | de gauche à droite. |
a ? true_value : false_value |
Expression ternaire | de gauche à droite. |
Données dans les jetons d'authentification
L'objet auth.token
peut contenir les valeurs suivantes :
Champ | Description |
---|---|
email |
Adresse e-mail associée au compte, le cas échéant. |
email_verified |
true si l'utilisateur a confirmé avoir accès à l'adresse email . Certains fournisseurs valident automatiquement les adresses e-mail qu'ils possèdent. |
phone_number |
Numéro de téléphone associé au compte, le cas échéant. |
name |
Nom à afficher de l'utilisateur, s'il est défini. |
sub |
ID utilisateur Firebase de l'utilisateur. Il est unique dans un projet. |
firebase.identities |
Dictionnaire de toutes les identités associées au compte de cet utilisateur. Les clés du dictionnaire peuvent être l'une des suivantes : email , phone , google.com , facebook.com , github.com , twitter.com . Les valeurs du dictionnaire sont des tableaux d'identifiants uniques pour chaque fournisseur d'identité associé au compte. Par exemple, auth.token.firebase.identities["google.com"][0] contient le premier ID utilisateur Google associé au compte. |
firebase.sign_in_provider |
Fournisseur de connexion utilisé pour obtenir ce jeton. Peut être l'une des chaînes suivantes : custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant |
ID du locataire associé au compte, le cas échéant. Par exemple : tenant2-m6tyz |
Champs supplémentaires dans les jetons d'identité JWT
Vous pouvez également accéder aux champs auth.token
suivants :
Revendications de jetons personnalisées | ||
---|---|---|
alg |
Algorithme | "RS256" |
iss |
Émetteur | Adresse e-mail du compte de service de votre projet |
sub |
Objet | Adresse e-mail du compte de service de votre projet |
aud |
Public visé | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Date/Heure d'émission | Heure actuelle, en secondes, depuis l'epoch UNIX |
exp |
Date/Heure d'expiration |
Durée, en secondes depuis l'époque UNIX, au bout de laquelle le jeton expire. Cette valeur peut correspondre à un maximum de 3 600 secondes après l'heure iat .
Remarque : Cette valeur ne contrôle que l'heure d'expiration du jeton personnalisé lui-même. Cependant, une fois que l'utilisateur est connecté à l'aide de signInWithCustomToken() , il reste connecté à l'appareil jusqu'à ce que sa session soit invalide ou qu'il se déconnecte.
|
<claims> (facultatif) |
Revendications personnalisées facultatives à inclure dans le jeton, accessibles via auth.token (ou request.auth.token ) dans les expressions. Par exemple, si vous créez une revendication personnalisée adminClaim , vous pouvez y accéder avec auth.token.adminClaim .
|
Étape suivante
- Firebase Data Connect fournit un SDK Admin qui vous permet d'effectuer des requêtes et des mutations à partir d'environnements privilégiés.
- Découvrez la sécurité IAM dans le guide de gestion des services et des bases de données.