Firebase Data Connect offre una solida sicurezza lato client con:
- Autorizzazione client web e mobile
- Controlli di autorizzazione individuali a livello di query e mutazione
- Attestazione dell'app con Firebase App Check.
Data Connect estende questa sicurezza con:
- Autorizzazione lato server
- Sicurezza del progetto Firebase e dell'utente Cloud SQL con IAM.
Autorizzare query e mutazioni del client
Data Connect è completamente integrato con Firebase Authentication, quindi puoi utilizzare dati avanzati sugli utenti che accedono ai tuoi dati (autenticazione) nella progettazione dei dati a cui possono accedere (autorizzazione).
Data Connect fornisce una direttiva @auth
per query e
mutazioni che consente di impostare il livello di autenticazione richiesto per autorizzare
l'operazione. Questa guida
introduce la direttiva @auth
, con esempi.
Inoltre, Data Connect supporta l'esecuzione di query incorporate nelle modifiche, in modo da poter recuperare criteri di autorizzazione aggiuntivi che hai archiviato nel database e utilizzarli nelle direttive @check
per decidere se le modifiche incluse sono autorizzate. Per questo caso di autorizzazione, la direttiva @redact
ti consente di controllare se i risultati della query vengono restituiti ai client nel
protocollo di trasferimento e la query incorporata omessa negli SDK generati. Trova l'introduzione a queste direttive, con esempi.
Comprendere la direttiva @auth
Puoi parametrizzare la direttiva @auth
per seguire uno dei diversi livelli di accesso preimpostati
che coprono molti scenari di accesso comuni. Questi livelli vanno da
PUBLIC
(che consente query e mutazioni da tutti i client senza
autenticazione di alcun tipo) a NO_ACCESS
(che non consente query e
mutazioni al di fuori degli ambienti server privilegiati che utilizzano l'SDK Firebase Admin). Ciascuno di questi livelli è correlato ai flussi di autenticazione
forniti da Firebase Authentication.
Livello | Definizione |
---|---|
PUBLIC |
L'operazione può essere eseguita da chiunque con o senza autenticazione. |
PUBLIC |
L'operazione può essere eseguita da chiunque con o senza autenticazione. |
USER_ANON |
Qualsiasi utente identificato, inclusi quelli che hanno eseguito l'accesso in modo anonimo con Firebase Authentication, è autorizzato a eseguire la query o la mutazione. |
USER |
Qualsiasi utente che ha eseguito l'accesso con Firebase Authentication è autorizzato a eseguire la query o la mutazione, ad eccezione degli utenti che hanno eseguito l'accesso in modo anonimo. |
USER_EMAIL_VERIFIED |
Qualsiasi utente che ha eseguito l'accesso con Firebase Authentication con un indirizzo email verificato è autorizzato a eseguire la query o la mutazione. |
NO_ACCESS |
Questa operazione non può essere eseguita al di fuori di un contesto SDK Admin. |
Utilizzando questi livelli di accesso preimpostati come punto di partenza, puoi definire controlli di autorizzazione complessi e robusti nella direttiva @auth
utilizzando i filtri where
e le espressioni Common Expression Language (CEL) valutate sul server.
Utilizza la direttiva @auth
per implementare scenari di autorizzazione comuni
I livelli di accesso preimpostati sono il punto di partenza per l'autorizzazione.
Il livello di accesso USER
è il livello di base più utile per iniziare.
L'accesso completamente sicuro si basa sul livello USER
, oltre a filtri ed espressioni
che controllano gli attributi utente, gli attributi risorsa, i ruoli e altri controlli. I livelli
USER_ANON
e USER_EMAIL_VERIFIED
sono varianti del caso USER
.
La sintassi delle espressioni consente di valutare i dati utilizzando un oggetto auth
che rappresenta
i dati di autenticazione passati con le operazioni, sia i dati standard nei token di autenticazione
sia i dati personalizzati nei token. Per l'elenco dei campi disponibili nell'oggetto auth
, consulta la sezione di riferimento.
Esistono ovviamente casi d'uso in cui PUBLIC
è il livello di accesso corretto da cui
iniziare. Ancora una volta, un livello di accesso è sempre un punto di partenza e sono necessari filtri ed espressioni aggiuntivi per una sicurezza solida.
Questa guida ora fornisce esempi di come creare su USER
e PUBLIC
.
Un esempio motivante
I seguenti esempi di best practice si riferiscono allo schema di una piattaforma di blogging con determinati contenuti bloccati dietro un piano di pagamento.
Una piattaforma di questo tipo probabilmente modellerebbe 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! @default(expr: "request.time")
updatedAt: Timestamp! @default(expr: "request.time")
}
Risorse di proprietà dell'utente
Firebase consiglia di scrivere filtri ed espressioni che testino la proprietà di una risorsa da parte dell'utente, nei seguenti casi, la proprietà di Posts
.
Negli esempi seguenti, i dati dei token di autenticazione vengono letti e confrontati utilizzando
espressioni. Il pattern tipico consiste nell'utilizzare espressioni come where: {authorUid:
{eq_expr: "auth.uid"}}
per confrontare un authorUid
memorizzato con l'auth.uid
(ID utente) passato nel token di autenticazione.
Crea
Questa pratica di autorizzazione inizia aggiungendo auth.uid
dal
token di autenticazione a ogni nuovo Post
come campo authorUid
per consentire il confronto nei
test di autorizzazione successivi.
# 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
})
}
Aggiorna
Quando un client tenta di aggiornare un Post
, puoi testare il auth.uid
trasmesso rispetto al authorUid
memorizzato.
# 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"
}
)
}
Elimina
La stessa tecnica viene utilizzata per autorizzare le operazioni di eliminazione.
# 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 }
}
Elenco
# 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
}
}
Filtraggio dei dati
Il sistema di autorizzazione di Data Connect ti consente di scrivere filtri sofisticati
combinati con livelli di accesso preimpostati come PUBLIC
, nonché utilizzando
i dati dei token di autenticazione.
Il sistema di autorizzazione ti consente anche di utilizzare solo espressioni, senza un livello di accesso di base, come mostrato in alcuni degli esempi seguenti.
Filtra per attributi risorsa
In questo caso, l'autorizzazione non si basa sui token di autenticazione, poiché il livello di sicurezza di base
è impostato su PUBLIC
. Tuttavia, possiamo impostare esplicitamente i record nel nostro database come
adatti all'accesso pubblico. Supponiamo di avere Post
record nel nostro database con
visibility
impostato su "pubblico".
# 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
}
}
Filtrare per rivendicazioni degli utenti
Supponiamo di aver configurato attestazioni utente personalizzate che vengono trasmesse nei token di autenticazione per identificare gli utenti con un piano "Pro" per la tua app, contrassegnati con un campo auth.token.plan
nel token di autenticazione. Le tue espressioni possono essere testate rispetto a questo 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
}
}
Filtra per ordine + limite
Oppure, potresti aver impostato visibility
nei record Post
per identificare i contenuti disponibili per gli utenti "Pro", ma per un elenco di dati di anteprima o teaser, limita ulteriormente il numero di record restituiti.
# 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
}
}
Filtra per ruolo
Se la tua rivendicazione personalizzata definisce un ruolo admin
, puoi testare e autorizzare le operazioni di conseguenza.
# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
posts { ...DisplayPost }
}
Aggiungi le direttive @check
e @redact
per cercare i dati di autorizzazione
Un caso d'uso comune dell'autorizzazione prevede l'archiviazione di ruoli di autorizzazione personalizzati nel database, ad esempio in una tabella di autorizzazioni speciali, e l'utilizzo di questi ruoli per autorizzare le modifiche per creare, aggiornare o eliminare i dati.
Utilizzando le ricerche dei dati di autorizzazione, puoi eseguire query per i ruoli in base a un ID utente e
utilizzare le espressioni CEL per decidere se la mutazione è autorizzata. Ad esempio, potresti
voler scrivere una mutazione UpdateMovieTitle
che consenta a un client autorizzato
di aggiornare i titoli dei film.
Per il resto di questa discussione, supponi che il database dell'app di recensioni di film memorizzi un
ruolo di autorizzazione in una tabella 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!
}
Utilizzo nelle mutazioni
Nell'implementazione di esempio riportata di seguito, la mutazione UpdateMovieTitle
include un campo query
per recuperare i dati da MoviePermission
e le seguenti direttive per garantire che l'operazione sia sicura e solida:
- Una direttiva
@transaction
per garantire che tutte le query e i controlli di autorizzazione vengano completati o non vadano a buon fine in modo atomico. - La direttiva
@redact
per omettere i risultati della query dalla risposta, il che significa che il nostro controllo dell'autorizzazione viene eseguito sul server Data Connect, ma i dati sensibili non vengono esposti al client. Una coppia di direttive
@check
per valutare la logica di autorizzazione sui risultati della query, ad esempio per verificare che un determinato ID utente disponga di un ruolo appropriato per apportare modifiche.
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
})
}
Utilizzare nelle query
Le ricerche dei dati di autorizzazione sono utili anche per limitare le query in base ai ruoli o ad altre limitazioni.
Nell'esempio seguente, che utilizza anche lo schema MoviePermission
, la
query verifica se un richiedente ha un ruolo "amministratore" appropriato per visualizzare gli utenti
che possono modificare 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
}
}
}
Antipattern da evitare nell'autorizzazione
La sezione precedente illustra i pattern da seguire quando si utilizza l'istruzione @auth
.
Devi anche essere a conoscenza di importanti antipattern da evitare.
Evita di trasmettere gli ID degli attributi utente e i parametri del token di autenticazione negli argomenti di query e mutazione
Firebase Authentication è un potente strumento per presentare i flussi di autenticazione e acquisire in modo sicuro i dati di autenticazione, come gli ID utente registrati e numerosi campi archiviati nei token di autenticazione.
Non è una pratica consigliata trasmettere ID utente e dati del token di autenticazione negli argomenti di query e mutazione.
# 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
}
}
Evitare di utilizzare il livello di accesso USER
senza filtri
Come discusso più volte nella guida, i livelli di accesso principali come USER
,
USER_ANON
, USER_EMAIL_VERIFIED
sono punti di partenza e di riferimento per
i controlli di autorizzazione, da migliorare con filtri ed espressioni. L'utilizzo di questi
livelli senza un filtro o un'espressione corrispondente che controlli quale utente
esegue la richiesta equivale essenzialmente all'utilizzo del livello PUBLIC
.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
documents {
id
title
text
}
}
Evita di utilizzare il livello di accesso PUBLIC
o USER
per la prototipazione
Per velocizzare lo sviluppo, può essere allettante impostare tutte le operazioni sul livello di accesso
PUBLIC
o sul livello di accesso USER
senza ulteriori miglioramenti per
autorizzare tutte le operazioni e consentirti di testare rapidamente il codice.
Dopo aver eseguito il prototipo iniziale in questo modo, inizia a passare da
NO_ACCESS
all'autorizzazione pronta per la produzione con i livelli PUBLIC
e USER
.
Tuttavia, non eseguirne il deployment come PUBLIC
o USER
senza aggiungere logica
aggiuntiva, come mostrato in questa guida.
# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
post: post_delete(
id: $id,
)
}
Evitare di basare l'autorizzazione su indirizzi email non verificati
Concedere l'accesso agli utenti di un determinato dominio è un ottimo modo per limitare l'accesso. Tuttavia, chiunque può dichiarare di essere il proprietario di un indirizzo email durante l'accesso. Assicurati di concedere l'accesso solo agli indirizzi email verificati tramite 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
})
}
Controlla anche 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
})
}
Controllare l'autorizzazione con la CLI Firebase
Come indicato in precedenza, i livelli di accesso preimpostati come PUBLIC
e
USER
sono il punto di partenza per un'autorizzazione efficace e
devono essere utilizzati con ulteriori controlli di autorizzazione basati su filtri ed espressioni.
Non devono essere utilizzate da sole senza un'attenta valutazione del caso d'uso.
Data Connect ti aiuta a controllare la tua strategia di autorizzazione analizzando
il codice del connettore quando esegui il deployment sul server utilizzando firebase deploy
dalla
CLI Firebase. Puoi utilizzare questo audit per esaminare la tua base di codice.
Quando esegui il deployment dei connettori, la CLI restituisce valutazioni per il codice operativo esistente, modificato e nuovo nel connettore.
Per le operazioni modificate e nuove, la CLI mostra avvisi e ti chiede la conferma quando utilizzi determinati livelli di accesso nelle nuove operazioni o quando modifichi le operazioni esistenti per utilizzare questi livelli di accesso.
Avvisi e prompt vengono sempre visualizzati per:
PUBLIC
Inoltre, vengono visualizzati avvisi e prompt nei seguenti livelli di accesso quando
non li aumenti con i filtri utilizzando auth.uid
:
USER
USER_ANON
USER_EMAIL_VERIFIED
Elimina gli avvisi di operazioni non sicure con l'argomento @auth(insecureReason:)
In molti casi, concluderai che l'utilizzo dei livelli di accesso PUBLIC
e USER*
è perfettamente appropriato.
Quando il connettore contiene molte operazioni, potresti volere un output di controllo della sicurezza più chiaro e pertinente che ometta le operazioni che normalmente attiverebbero un avviso, ma sai che hanno il livello di accesso corretto.
Puoi sopprimere gli avvisi per queste operazioni con @auth(insecureReason:)
.
Ad esempio:
query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
{
items {
id name
}
}
Utilizzare Firebase App Check per l'attestazione dell'app
L'autenticazione e l'autorizzazione sono componenti fondamentali della sicurezza di Data Connect. L'autenticazione e l'autorizzazione combinate con l'attestazione dell'app costituiscono una soluzione di sicurezza molto solida.
Con l'attestazione tramite Firebase App Check, i dispositivi che eseguono la tua app utilizzeranno un fornitore di attestazione di app o dispositivi che attesta che le operazioni Data Connect hanno origine dalla tua app autentica e che le richieste provengono da un dispositivo autentico e non manomesso. Questa attestazione è allegata a ogni richiesta che la tua app invia a Data Connect.
Per scoprire come attivare App Check per Data Connect e includere il relativo SDK client nella tua app, dai un'occhiata alla App Check panoramica.
Livelli di autenticazione per la direttiva @auth(level)
La tabella seguente elenca tutti i livelli di accesso standard e i relativi equivalenti CEL. I livelli di autenticazione sono elencati in ordine decrescente: ogni livello comprende tutti gli utenti che corrispondono ai livelli successivi.
Livello | Definizione |
---|---|
PUBLIC |
L'operazione può essere eseguita da chiunque con o senza autenticazione.
Considerazioni:i dati possono essere letti o modificati da qualsiasi utente. Firebase consiglia questo livello di autorizzazione per i dati consultabili pubblicamente, come le schede di prodotti o contenuti multimediali. Consulta gli esempi di best practice e le alternative. Equivalente a @auth(expr: "true")
I filtri e le espressioni @auth non possono essere utilizzati in combinazione
con questo livello di accesso. Queste espressioni non andranno a buon fine e restituiranno l'errore 400 Bad
Request.
|
USER_ANON |
Qualsiasi utente identificato, inclusi quelli che hanno eseguito l'accesso in modo anonimo
con Firebase Authentication, è autorizzato a eseguire la query o la mutazione.
Nota: USER_ANON è un superset di USER .
Considerazioni:tieni presente che devi progettare con attenzione le query e le mutazioni per questo livello di autorizzazione. Questo livello consente all'utente di accedere in modo anonimo (accesso automatico collegato solo a un dispositivo utente) con Authentication e non esegue di per sé altri controlli, ad esempio se i dati appartengono all'utente. Consulta gli esempi di best practice e le alternative. Poiché i flussi di accesso anonimi di Authentication emettono un uid , il
livello USER_ANON è equivalente a
@auth(expr: "auth.uid != nil")
|
USER |
Qualsiasi utente che ha eseguito l'accesso con Firebase Authentication è autorizzato a
eseguire la query o la mutazione, ad eccezione degli utenti che hanno eseguito l'accesso in modo anonimo.
Considerazioni:tieni presente che devi progettare con attenzione le query e le mutazioni per questo livello di autorizzazione. Questo livello verifica solo che l'utente abbia eseguito l'accesso con Authentication e non esegue altri controlli, ad esempio se i dati appartengono all'utente. Consulta gli esempi di best practice e le alternative. Equivalente a @auth(expr: "auth.uid != nil &&
auth.token.firebase.sign_in_provider != 'anonymous'")"
|
USER_EMAIL_VERIFIED |
Qualsiasi utente che ha eseguito l'accesso con Firebase Authentication con un indirizzo email verificato è autorizzato a eseguire la query o la mutazione.
Considerazioni:poiché la verifica email viene eseguita utilizzando Authentication, si basa su un metodo Authentication più solido, quindi questo livello offre una maggiore sicurezza rispetto a USER o
USER_ANON . Questo livello verifica solo che l'utente abbia eseguito l'accesso
con Authentication con un indirizzo email verificato e non esegue
altri controlli, ad esempio, se i dati appartengono all'utente. Consulta gli
esempi di best practice e le alternative.
Equivalente a @auth(expr: "auth.uid != nil &&
auth.token.email_verified")" |
NO_ACCESS |
Questa operazione non può essere eseguita al di fuori di un contesto SDK Admin.
Equivalente a @auth(expr: "false") |
Riferimento CEL per @auth(expr)
Come mostrato negli esempi in altre sezioni di questa guida, puoi e devi utilizzare
le espressioni definite in Common Expression Language (CEL) per controllare l'autorizzazione
per Data Connect utilizzando le direttive @auth(expr:)
e @check
.
Questa sezione illustra la sintassi CEL pertinente per la creazione di espressioni per queste direttive.
Le informazioni di riferimento complete per CEL sono fornite nella specifica CEL.
Variabili di test trasmesse in query e mutazioni
La sintassi @auth(expr)
ti consente di accedere e testare le variabili di query e mutazioni.
Ad esempio, puoi includere una variabile di operazione, ad esempio $status
, utilizzando
vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")
Dati disponibili per le espressioni: request, response, this
Utilizzi i dati per:
- Valutazione con espressioni CEL nelle direttive
@auth(expr:)
e@check(expr:)
- Assegnazione mediante espressioni del server,
<field>_expr
.
Entrambe le espressioni CEL @auth(expr:)
e @check(expr:)
possono valutare quanto segue:
request.operationName
vars
(alias perrequest.variables
)auth
(alias perrequest.auth
)
Nelle mutazioni, puoi accedere e assegnare i contenuti di:
response
(per controllare i risultati parziali nella logica multi-step)
Inoltre, le espressioni @check(expr:)
possono valutare:
this
(il valore del campo corrente)response
(per controllare i risultati parziali nella logica multi-step)
Il binding request.operationName
Il binding request.operarationName
memorizza il tipo di operazione, query
o mutazione.
Il binding vars
(request.vars)
L'associazione vars
consente alle espressioni di accedere a tutte le variabili
trasmesse nella query o nella mutazione.
Puoi utilizzare vars.<variablename>
in un'espressione come alias per
request.variables.<variablename>
completo:
# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")
Il binding auth
(request.auth)
Authentication identifica gli utenti che richiedono l'accesso ai tuoi dati e fornisce queste informazioni come binding su cui puoi basarti nelle tue espressioni.
Nei filtri e nelle espressioni, puoi utilizzare auth
come alias per
request.auth
.
Il binding di autorizzazione contiene le seguenti informazioni:
uid
: un ID utente univoco assegnato all'utente richiedente.token
: una mappa dei valori raccolti da Authentication.
Per maggiori dettagli sui contenuti di auth.token
, vedi
Dati nei token di autenticazione
L'associazione response
Il binding response
contiene i dati assemblati dal server in
risposta a una query o una mutazione man mano che vengono assemblati.
Man mano che l'operazione procede, quando ogni passaggio viene completato correttamente,
response
contiene i dati di risposta dei passaggi completati correttamente.
Il binding response
è strutturato in base alla forma dell'operazione associata, inclusi campi nidificati (multipli) e query incorporate (se applicabile).
Tieni presente che quando accedi ai dati di risposta alle query incorporate, i campi possono contenere
qualsiasi tipo di dati, a seconda dei dati richiesti nella query incorporata; quando
accedi ai dati restituiti dai campi di mutazione come _insert
e _delete
, questi possono
contenere chiavi UUID, numero di eliminazioni, valori null (vedi il
riferimento alle mutazioni).
Ad esempio:
- In una mutazione che contiene una query incorporata, il binding
response
contiene i dati di ricerca inresponse.query.<fieldName>.<fieldName>....
, in questo 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
}
}
}
- In una mutazione in più passaggi, ad esempio con più campi
_insert
, il bindingresponse
contiene dati parziali inresponse.<fieldName>.<fieldName>....
, in questo 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.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
content: $itemContent,
})
}
L'associazione this
L'associazione this
restituisce il campo a cui è collegata l'istruzione @check
. In un caso di base, potresti valutare i risultati di query a valore singolo.
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 il campo restituito si verifica più volte perché un antenato è un elenco, ogni
occorrenza viene testata con this
associato a ogni valore.
Per un determinato percorso, se un antenato è null
o []
, il campo non verrà
raggiunto e la valutazione CEL verrà ignorata per quel percorso. In altre parole, la valutazione viene eseguita solo quando this
è null
o non null
, ma mai undefined
.
Quando il campo stesso è un elenco o un oggetto, this
segue la stessa struttura
(inclusi tutti i discendenti selezionati in caso di oggetti), come illustrato nell'esempio
seguente.
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
})
}
Sintassi delle espressioni complesse
Puoi scrivere espressioni più complesse combinandole con gli operatori &&
e ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")
La sezione seguente descrive tutti gli operatori disponibili.
Operatori e precedenza degli operatori
Utilizza la seguente tabella come riferimento per gli operatori e la relativa precedenza.
Date le espressioni arbitrarie a
e b
, un campo f
e un indice i
.
Operatore | Descrizione | Associatività |
---|---|---|
a[i] a() a.f |
Accesso a indice, chiamata e campo | da sinistra a destra |
!a -a |
Negazione unaria | da destra a sinistra |
a/b a%b a*b |
Operatori moltiplicativi | da sinistra a destra |
a+b a-b |
Operatori additivi | da sinistra a destra |
a>b a>=b a<b a<=b |
Operatori relazionali | da sinistra a destra |
a in b |
Esistenza nell'elenco o nella mappa | da sinistra a destra |
type(a) == t |
Confronto tra tipi, in cui t può essere bool, int, float,
number, string, list, map, timestamp o duration |
da sinistra a destra |
a==b a!=b |
Operatori di confronto | da sinistra a destra |
a && b |
AND condizionale | da sinistra a destra |
a || b |
OR condizionale | da sinistra a destra |
a ? true_value : false_value |
Espressione ternaria | da sinistra a destra |
Dati nei token di autorizzazione
L'oggetto auth.token
può contenere i seguenti valori:
Campo | Descrizione |
---|---|
email |
L'indirizzo email associato all'account, se presente. |
email_verified |
true se l'utente ha verificato di avere accesso all'indirizzo email . Alcuni provider verificano automaticamente gli indirizzi email di loro proprietà. |
phone_number |
Il numero di telefono associato all'account, se presente. |
name |
Il nome visualizzato dell'utente, se impostato. |
sub |
L'UID Firebase dell'utente. Questo valore è univoco all'interno di un progetto. |
firebase.identities |
Dizionario di tutte le identità associate all'account di questo utente. Le chiavi del dizionario possono essere una delle seguenti: email , phone , google.com , facebook.com , github.com , twitter.com . I valori del dizionario sono array di identificatori univoci per ogni provider di identità associato all'account. Ad esempio, auth.token.firebase.identities["google.com"][0] contiene il primo ID utente Google associato all'account. |
firebase.sign_in_provider |
Il provider di accesso utilizzato per ottenere questo token. Può essere una delle seguenti stringhe: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant |
L'ID tenant associato all'account, se presente. Ad esempio, tenant2-m6tyz |
Campi aggiuntivi nei token ID JWT
Puoi anche accedere ai seguenti campi auth.token
:
Attestazioni token personalizzate | ||
---|---|---|
alg |
Algoritmo | "RS256" |
iss |
Emittente | L'indirizzo email del service account del tuo progetto |
sub |
Oggetto | L'indirizzo email del service account del tuo progetto |
aud |
Pubblico | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Ora di emissione | L'ora attuale, in secondi a partire dall'epoca UNIX |
exp |
Scadenza |
Il momento in cui il token scade, espresso in secondi a partire dal tempo Unix. Può
essere al massimo 3600 secondi dopo iat .
Nota: questo valore controlla solo la scadenza del token personalizzato. Tuttavia, una volta che un utente ha eseguito l'accesso utilizzando signInWithCustomToken() , l'accesso rimarrà attivo sul
dispositivo finché la sessione non viene invalidata o l'utente non esce.
|
(Facoltativo) <claims> |
Rivendicazioni personalizzate facoltative da includere nel token, a cui è possibile accedere tramite
auth.token (o request.auth.token ) nelle
espressioni. Ad esempio, se crei un'attestazione personalizzata
adminClaim , puoi accedervi con
auth.token.adminClaim .
|
Passaggi successivi
- Firebase Data Connect fornisce un SDK Admin per consentirti di eseguire query e mutazioni da ambienti privilegiati.
- Scopri di più sulla sicurezza IAM nella guida alla gestione di servizi e database.