Bezpieczne łączenie danych z autoryzacją i potwierdzeniem

Firebase Data Connect zapewnia niezawodne zabezpieczenia po stronie klienta:

  • Autoryzacja klienta mobilnego i internetowego
  • Kontrola dostępu na poziomie poszczególnych zapytań i mutacji
  • Potwierdzenie aplikacji przez Firebase App Check.

Data Connect zapewnia dodatkowe zabezpieczenia:

  • Autoryzacja po stronie serwera
  • Bezpieczeństwo użytkownika projektu Firebase i Cloud SQL za pomocą usługi IAM.

Autoryzowanie zapytań i mutacji klienta

Data Connect jest w pełni zintegrowany z Firebase Authentication, dzięki czemu możesz używać w swoim projekcie rozbudowanych danych o użytkownikach, którzy uzyskują dostęp do Twoich danych (uwierzytelnianie), oraz o danych, do których mają oni dostęp (autoryzacja).

Data Connect udostępnia dyrektywę @auth dla zapytań i mutacji, która umożliwia ustawienie poziomu uwierzytelniania wymaganego do autoryzacji operacji. Ten przewodnik prezentuje dyrektywę @auth z przykładami.

Ponadto Data Connect obsługuje wykonywanie zapytań zawartych w mutacjach, dzięki czemu możesz pobierać dodatkowe kryteria autoryzacji zapisane w bazie danych i używać ich w instrukcjach @check, aby określić, czy otoczone mutacje są autoryzowane. W tym przypadku autoryzacji dyrektywa @redact pozwala Ci określić, czy wyniki zapytań mają być zwracane do klientów w ramach protokołu sieciowego, a wbudowane zapytanie ma być pomijane w wygenerowanych pakietach SDK. Znajdź wprowadzenie do tych dyrektyw z przykładami.

Informacje o dyrektywie @auth

Możesz skonfigurować dyrektywę @auth, aby korzystała z jednego z kilku wstępnie zdefiniowanych poziomów dostępu, które obejmują wiele typowych scenariuszy dostępu. Te poziomy sięgają od PUBLIC (co pozwala na żądania i mutacje ze wszystkich klientów bez uwierzytelniania) do NO_ACCESS (co uniemożliwia żądania i mutacje poza uprzywilejowanymi środowiskami serwerów korzystającymi z pakietu Admin SDK Firebase). Każdy z tych poziomów jest powiązany z procesami uwierzytelniania udostępnianymi przez Firebase Authentication.

Poziom Definicja
PUBLIC Operację może wykonać dowolna osoba z uwierzytelnieniem lub bez niego.
PUBLIC Operację może wykonać dowolna osoba z uwierzytelnieniem lub bez niego.
USER_ANON Każdy zidentyfikowany użytkownik, w tym użytkownicy, którzy zalogowali się anonimowo za pomocą Firebase Authentication, może wykonać zapytanie lub mutację.
USER Każdy użytkownik zalogowany za pomocą Firebase Authentication może wykonać zapytanie lub mutację, z wyjątkiem użytkowników logujących się anonimowo.
USER_EMAIL_VERIFIED Każdy użytkownik, który zalogował się za pomocą Firebase Authentication z weryfikowanym adresem e-mail, może wykonać zapytanie lub mutację.
NO_ACCESS Tej operacji nie można wykonać poza kontekstem pakietu SDK Admin.

Korzystając z tych wstępnie zdefiniowanych poziomów dostępu, możesz definiować złożone i solidne mechanizmy autoryzacji w instrukcji @auth za pomocą filtrów where i wyrażeń w języku CEL (Common Expression Language) ocenianych na serwerze.

Używanie dyrektywy @auth do implementowania typowych scenariuszy autoryzacji

Wstępnie ustawione poziomy dostępu stanowią punkt wyjścia dla autoryzacji.

Poziom dostępu USER to najczęściej używany poziom podstawowy.

Pełny dostęp zabezpieczony będzie opierać się na poziomie USER oraz filtrach i wyrażeniach, które sprawdzają atrybuty użytkownika, atrybuty zasobu, role i inne. Poziomy USER_ANONUSER_EMAIL_VERIFIED to odmiany przypadku USER.

Składnia wyrażeń umożliwia analizowanie danych za pomocą obiektu auth, który reprezentuje dane uwierzytelniania przekazywane z operacjami, zarówno standardowe dane w tokenach uwierzytelniania, jak i dane niestandardowe w tokenach. Listę pól dostępnych w obiekcie auth znajdziesz w sekcji z informacjami referencyjnymi.

Oczywiście są przypadki, w których PUBLIC jest odpowiednim poziomem dostępu na początek. Ponownie przypominamy, że poziom dostępu to zawsze punkt wyjścia, a do zapewnienia solidnej ochrony potrzebne są dodatkowe filtry i wyrażenia.

Ten przewodnik zawiera teraz przykłady wykorzystania funkcji USERPUBLIC.

Motywujący przykład

Poniższe przykłady sprawdzonych metod odnoszą się do schematu platformy blogowej, w której niektóre treści są dostępne tylko dla użytkowników z subskrypcją płatną.

Taka platforma prawdopodobnie modelowałaby UsersPosts.

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")
}

Zasoby należące do użytkownika

Firebase zaleca pisanie filtrów i wyrażeń, które sprawdzają, czy użytkownik jest właścicielem zasobu. W tych przypadkach jest to Posts.

W podanych niżej przykładach dane z tokenów autoryzacji są odczytywane i porównywane za pomocą wyrażeń. Typowy schemat to użycie wyrażenia takiego jak where: {authorUid: {eq_expr: "auth.uid"}} do porównania zapisanego authorUidauth.uid (identyfikatorem użytkownika) przekazanym w tokenie uwierzytelniania.

Utwórz

Ta metoda autoryzacji zaczyna się od dodania auth.uid z tokena autoryzacji do każdego nowego Post jako pola authorUid, aby umożliwić porównanie w testach autoryzacji podrzędnych.

# 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
  })
}
Zaktualizuj

Gdy klient próbuje zaktualizować Post, możesz przetestować przekazaną wartość auth.uid w porównaniu z zapisaną wartością authorUid.

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

Ta sama metoda jest używana do autoryzacji operacji usuwania.

# 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
    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
  }
}

Filtrowanie danych

System autoryzacji Data Connect umożliwia tworzenie zaawansowanych filtrów w połączeniu z wstępnie ustawionymi poziomami dostępu, takimi jak PUBLIC, a także za pomocą danych z tokenów autoryzacji.

System autoryzacji umożliwia też używanie wyrażeń bez poziomu dostępu do bazy danych, jak pokazano w niektórych z następujących przykładów.

Filtrowanie według atrybutów zasobu

W tym przypadku autoryzacja nie jest oparta na tokenach uwierzytelniania, ponieważ poziom bezpieczeństwa podstawowy ma wartość PUBLIC. Możemy jednak wyraźnie ustawić w naszej bazie danych rekordy jako odpowiednie do publicznego dostępu. Załóżmy, że mamy w naszej bazie danych Post rekordów z visibility ustawionym jako „publiczny”.

# 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
  }
}
Filtrowanie według roszczeń użytkownika

Załóżmy, że masz skonfigurowane niestandardowe oświadczenia użytkownika, które przekazują tokeny uwierzytelniające, aby zidentyfikować użytkowników w ramach abonamentu „Pro” w Twojej aplikacji. W polu auth.token.plan w tokenie uwierzytelniającym jest oznaczony odpowiednim flagą. Wyrażenia mogą być testowane pod kątem tego pola.

# 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
  }
}
Filtrowanie według kolejności i limitu

Możesz też ustawić w rekordach visibility wartość visibility, aby wskazać, że są one dostępne dla użytkowników „pro”, ale w celu wyświetlenia podglądu lub listy danych z teaserem możesz ograniczyć liczbę zwracanych rekordów.Post

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

Jeśli roszczenie niestandardowe definiuje rolę admin, możesz testować i autoryzować operacje.

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

Zapoznanie się z dyrektywami @check@redact

Dyrektywa @check sprawdza, czy określone pola występują w wynikach zapytania. Do testowania wartości pól służy wyrażenie w języku Common Expression Language (CEL). Domyślne działanie tej dyrektywy polega na sprawdzaniu i odrzucaniu węzłów o wartości null.

Dyrektywa @redact zastępuje część odpowiedzi od klienta. Pole zanonimizowane jest nadal oceniane pod kątem efektów ubocznych (w tym zmian danych i @check), a jego wyniki są nadal dostępne dla kolejnych kroków w wyrażeniach CEL.

Data Connect dyrektywy @check@redact są najczęściej używane w kontekście kontroli autoryzacji. Zapoznaj się z omówieniem wyszukiwania danych autoryzacji.

.

Dodaj dyrektywy @check@redact, aby sprawdzić dane autoryzacji

Typowym przypadkiem użycia autoryzacji jest przechowywanie niestandardowych ról autoryzacji w bazie danych, na przykład w specjalnej tabeli uprawnień, i używanie tych ról do autoryzowania mutacji w celu tworzenia, aktualizowania lub usuwania danych.

Za pomocą wyszukiwania danych autoryzacyjnych możesz wysyłać zapytania o role na podstawie identyfikatora użytkownika i używać wyrażeń CEL, aby określić, czy mutacja jest autoryzowana. Możesz na przykład napisać funkcję UpdateMovieTitle, która pozwala autoryzowanemu klientowi aktualizować tytuły filmów.

W dalszej części tej dyskusji przyjmijmy, że baza danych aplikacji do recenzowania filmów przechowuje rolę autoryzacji w tabeli MoviePermission.

# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["doc", "userId"]) {
  movie: Movie! # implies another field: movieId: UUID!
  userId: String! # Can also be a reference to a User table, doesn't matter
  role: String!
}

W tym przykładzie implementacji mutacja UpdateMovieTitle zawiera pole query, które służy do pobierania danych z tabeli MoviePermission, oraz te dyrektywy, które zapewniają bezpieczeństwo i stabilność operacji:

  • Dyrektywa @transaction, która zapewnia, że wszystkie zapytania i sprawdzenia autoryzacji są wykonywane lub nie są wykonywane w ramach jednej operacji.
  • Dyrektywa @redact służy do pomijania wyników zapytań w odpowiedzi, co oznacza, że weryfikacja autoryzacji jest wykonywana na serwerze Data Connect, ale dane wrażliwe nie są udostępniane klientowi.
  • Para dyrektyw @check służących do oceny logiki autoryzacji na podstawie wyników zapytania, np. sprawdzania, czy dany identyfikator użytkownika ma odpowiednią rolę do wprowadzania modyfikacji.

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
  })
}

Nieprawidłowe rozwiązania dotyczące autoryzacji

W poprzedniej sekcji omówiliśmy wzorce, których należy używać podczas stosowania dyrektywy @auth.

Warto też wiedzieć, jakich wzorców unikać.

Unikaj przekazywania identyfikatorów atrybutów użytkownika i parametrów tokena autoryzacji w argumentach zapytania i mutacji

Firebase Authentication to potężne narzędzie do prezentowania przepływów uwierzytelniania i bezpiecznego rejestrowania danych uwierzytelniania, takich jak zarejestrowane identyfikatory użytkowników i liczne pola przechowywane w tokenach uwierzytelniania.

Nie zalecamy przekazywania identyfikatorów użytkowników ani danych tokena autoryzacji w argumentach zapytań i mutacji.

# 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
  }
}

Unikaj używania poziomu dostępu USER bez żadnych filtrów

Jak już kilkakrotnie wspominaliśmy w tym przewodniku, podstawowe poziomy dostępu, takie jak USER, USER_ANONUSER_EMAIL_VERIFIED, stanowią punkt wyjścia dla kontroli autoryzacji, które można wzbogacić za pomocą filtrów i wyrażeń. Używanie tych poziomów bez odpowiedniego filtra lub wyrażenia, które sprawdza, który użytkownik wykonuje żądanie, jest zasadniczo równoznaczne z używaniem poziomu PUBLIC.

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

Unikaj korzystania z poziomu dostępu PUBLIC lub USER na potrzeby prototypowania

Aby przyspieszyć rozwój, możesz być skłonny ustawić wszystkie operacje na poziomie dostępu PUBLIC lub USER bez dalszych ulepszeń, aby autoryzować wszystkie operacje i umożliwić szybkie testowanie kodu.

Gdy wykonasz w ten sposób wstępny prototyp, zacznij przechodzić z poziomu NO_ACCESS do autoryzacji gotowej do wdrożenia z poziomami PUBLICUSER. Nie należy jednak stosować ich jako PUBLIC ani USER bez dodania dodatkowej logiki, jak w tym przewodniku.

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

Używanie aplikacji Firebase App Check do weryfikacji aplikacji

Uwierzytelnianie i autoryzacja to kluczowe elementy bezpieczeństwaData Connect. Uwierzytelnianie i autoryzacja w połączeniu z potwierdzeniem aplikacji stanowią bardzo solidne rozwiązanie zabezpieczające.

W przypadku uwierzytelniania za pomocą Firebase App Check urządzenia z Twoją aplikacją będą używać dostawcy uwierzytelniania aplikacji lub urządzenia, który potwierdza, że operacje Data Connect pochodzą z autentycznej aplikacji, a żądania pochodzą z autentycznego, nienaruszonego urządzenia. To zaświadczenie jest dołączane do każdego żądania wysyłanego przez Twoją aplikację do Data Connect.

Aby dowiedzieć się, jak włączyć App Check w przypadku Data Connect i umieścić w aplikacji pakiet klienta SDK App Check, przeczytaj omówienie App Check.

Poziomy uwierzytelniania w przypadku dyrektywy @auth(level)

Tabela poniżej zawiera wszystkie standardowe poziomy dostępu i ich odpowiedniki w CEL. Poziomy uwierzytelniania są wyświetlane od najszerszego do najwęższych – każdy poziom obejmuje wszystkich użytkowników, którzy pasują do podanych poziomów.

Poziom Definicja
PUBLIC Operację może wykonać dowolna osoba z uwierzytelnieniem lub bez niego.

Uwagi: dane mogą być odczytywane i modyfikowane przez dowolnego użytkownika. Firebase zaleca ten poziom autoryzacji w przypadku danych publicznie dostępnych, takich jak informacje o produktach lub mediach. Zapoznaj się z przykładami i alternatywami sprawdzonych metod.

Jest to równoważne z poziomem dostępu @auth(expr: "true")

@authFiltry i wyrażenia nie mogą być używane w połączeniu z tym poziomem dostępu. W przypadku takich wyrażeń zostanie zwrócony błąd 400 Nieprawidłowe żądanie.
USER_ANON Każdy zidentyfikowany użytkownik, w tym użytkownicy, którzy zalogowali się anonimowo za pomocą Firebase Authentication, może wykonać zapytanie lub mutację.

Uwaga: USER_ANON jest superzbiorem zbioru USER.

Uwagi: musisz dokładnie zaprojektować zapytania i mutacje pod kątem tego poziomu autoryzacji. Ten poziom umożliwia użytkownikowi zalogowanie się anonimnie (automatyczne logowanie powiązane tylko z urządzeniem użytkownika) za pomocą Authentication. Nie wykonuje on samodzielnie żadnych innych kontroli, na przykład czy dane należą do użytkownika. Zapoznaj się z przykładami sprawdzonych metod i alternatywami.

Ponieważ anonimowe procesy logowania Authentication generują odpowiedź uid, poziom USER_ANON jest równoważny poziomowi
@auth(expr: "auth.uid != nil")
USER Każdy użytkownik zalogowany za pomocą Firebase Authentication może wykonać zapytanie lub mutację, z wyjątkiem użytkowników logujących się anonimowo.

Uwagi: musisz dokładnie zaprojektować zapytania i mutacje pod kątem tego poziomu autoryzacji. Na tym poziomie sprawdzane jest tylko to, czy użytkownik jest zalogowany za pomocą Authentication, a nie wykonuje samodzielnie innych kontroli, na przykład czy dane należą do tego użytkownika. Zapoznaj się ze sprawdzonymi metodami i alternatywami.

Odpowiednik: @auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")"
USER_EMAIL_VERIFIED Każdy użytkownik, który zalogował się za pomocą Firebase Authentication z weryfikowanym adresem e-mail, może wykonać zapytanie lub mutację.

Uwagi: weryfikacja e-maila jest wykonywana za pomocą Authentication, co oznacza, że opiera się na bardziej niezawodnej metodzie Authentication, a więc zapewnia dodatkową ochronę w porównaniu z USER lub USER_ANON. Ten poziom sprawdza tylko, czy użytkownik jest zalogowany za pomocą Authentication z weryfikowanego adresu e-mail, ale nie wykonuje samodzielnie innych kontroli, na przykład czy dane należą do użytkownika. Zapoznaj się z przykładami i alternatywami sprawdzonych metod.

Odpowiednik: @auth(expr: "auth.uid != nil && auth.token.email_verified")"
NO_ACCESS Tej operacji nie można wykonać poza kontekstem pakietu SDK Admin.

Odpowiednik: @auth(expr: "false")

Przykłady wyrażeń CEL w przypadku funkcji @auth(expr)@check(expr)

Jak widać w przykładach w tym przewodniku, możesz i powinieneś używać wyrażeń zdefiniowanych w języku Common Expression Language (CEL) do kontrolowania autoryzacji Data Connect za pomocą dyrektyw @auth(expr:)@check.

W tej sekcji omawiamy składnię CEL, która jest przydatna do tworzenia wyrażeń do tych dyrektyw.

Pełne informacje referencyjne dotyczące CEL znajdziesz w specyfikacji CEL.

Testowanie zmiennych przekazywanych w zapytaniach i mutacjach

Składnia @auth(expr) umożliwia dostęp do zmiennych z zapytań i mutacji oraz ich testowanie.

Możesz na przykład użyć zmiennej operacji, takiej jak $status, za pomocą parametru vars.status.

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

Dane dostępne dla wyrażeń

Zarówno wyrażenia CEL @auth(expr:), jak i @check(expr:) mogą oceniać te elementy:

  • request.operationName
  • vars (alias do request.variables)
  • auth (alias do request.auth)

Dodatkowo wyrażenia @check(expr:) mogą oceniać:

  • this (wartość bieżącego pola)

Obiekt request.operationName

Obiekt request.operarationName przechowuje typ operacji, czyli zapytanie lub mutację.

Obiekt vars

Obiekt vars umożliwia wyrażeniom dostęp do wszystkich zmiennych przekazanych w zapytaniu lub mutacji.

Możesz użyć w wyrażeniu atrybutu vars.<variablename> jako aliasu pełnego atrybutu request.variables.<variablename>:

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

Obiekt auth

Authentication identyfikuje użytkowników, którzy proszą o dostęp do Twoich danych, i przekazuje te informacje jako obiekt, na podstawie którego możesz budować swoje wyrażenia.

W filtrach i wyrażeniach możesz używać wartości auth jako aliasu wartości request.auth.

Obiekt auth zawiera te informacje:

  • uid: unikalny identyfikator użytkownika przypisany do użytkownika przesyłającego żądanie.
  • token: mapa wartości zebranych przez Authentication.

Więcej informacji o zawartości pola auth.token znajdziesz w artykule Dane w tokenach uwierzytelniania.

Powiązanie this

Powiązanie this zwraca pole, do którego jest przypisane pole dyrektywy @check. W najprostszym przypadku możesz analizować wyniki zapytania o pojedynczej wartości.

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
  })
}

Jeśli zwracane pole występuje kilka razy, ponieważ każdy z jego przodków jest listą, każde wystąpienie jest testowane z użyciem this powiązanego z każdą wartością.

W przypadku dowolnej ścieżki, jeśli jeden z jej elementów jest elementem nadrzędnym null lub [], nie dojdzie do odwołania się do pola i ocena CEL zostanie pominięta. Inaczej mówiąc, sprawdzanie odbywa się tylko wtedy, gdy this ma wartość null lub nie-null, ale nigdy undefined.

Gdy samo pole jest listą lub obiektem, this ma taką samą strukturę (w tym wszystkich potomków wybranych w przypadku obiektów), jak w tym przykładzie.

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
  })
}

Składnia wyrażeń złożonych

Aby tworzyć bardziej złożone wyrażenia, możesz łączyć operatory &&||.

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

W sekcji poniżej opisujemy wszystkie dostępne operatory.

Operatory i pierwotność operatorów

Poniższa tabela zawiera informacje o operatorach i odpowiednim dla nich priorytecie.

Załóżmy dowolne wyrażenia ab, pole f oraz indeks i.

Operator Opis Związek
a[i] a() a.f Indeksowanie, wywoływanie i dostęp do pól od lewej do prawej.
!a -a Negacja unarna od prawej do lewej
a/b a%b a*b Operatory mnożenia od lewej do prawej.
a+b a-b Operatory addytywne od lewej do prawej.
a>b a>=b a<b a<=b Operatory relacji od lewej do prawej.
a in b występowanie na liście lub mapie; od lewej do prawej.
type(a) == t Porównanie typu, gdzie t może być wartością logiczną, liczbą całkowitą, liczbą zmiennoprzecinkową, liczbą, ciągiem znaków, listą, mapą, sygnaturą czasową lub czasem trwania. od lewej do prawej.
a==b a!=b Operatory porównania od lewej do prawej.
a && b Warunkowe AND od lewej do prawej.
a || b Warunkowe LUB od lewej do prawej.
a ? true_value : false_value Wyrażenie warunkowe od lewej do prawej.

Dane w tokenach uwierzytelniania

Obiekt auth.token może zawierać te wartości:

Pole Opis
email adres e-mail powiązany z kontem (jeśli istnieje).
email_verified true, jeśli użytkownik potwierdził, że ma dostęp do adresu email. Niektórzy dostawcy automatycznie weryfikują należące do siebie adresy e-mail.
phone_number numer telefonu powiązany z kontem (jeśli istnieje);
name Wyświetlana nazwa użytkownika (jeśli została ustawiona).
sub Identyfikator UID użytkownika w Firebase. Musi być niepowtarzalny w ramach projektu.
firebase.identities Słownik wszystkich tożsamości powiązanych z kontem tego użytkownika. Klucze słownika mogą być dowolnymi z tych elementów: email, phone, google.com, facebook.com, github.com, twitter.com. Wartości w słowniku to tablice unikalnych identyfikatorów dla każdego dostawcy tożsamości powiązanego z kontem. Na przykład auth.token.firebase.identities["google.com"][0] zawiera pierwsze identyfikator użytkownika Google powiązany z kontem.
firebase.sign_in_provider Dostawca logowania użyty do uzyskania tego tokena. Może być dowolnym z tych ciągów: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant Identyfikator tenantId powiązany z kontem (jeśli występuje). na przykład tenant2-m6tyz.

Dodatkowe pola w tokenach identyfikacyjnych JWT

Możesz też uzyskać dostęp do tych pól auth.token:

Deklaracje tokenów niestandardowych
alg Algorytm "RS256"
iss Wystawca Adres e-mail konta usługi projektu
sub Temat Adres e-mail konta usługi projektu
aud Odbiorcy "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Issued-at time Bieżący czas w sekundach od początku epoki UNIX
exp Okres ważności Czas w sekundach od początku epoki UNIX, w którym token traci ważność. Może ona nastąpić maksymalnie 3600 sekund później niż iat.
Uwaga: to ustawienie określa tylko czas wygaśnięcia tokenu niestandardowego. Jednak po zalogowaniu użytkownika za pomocą opcji signInWithCustomToken() będzie on zalogowany na urządzeniu do czasu unieważnienia sesji lub wylogowania się.
<claims> (opcjonalnie) Opcjonalne oświadczenia niestandardowe do uwzględnienia w tokenie, do których można uzyskać dostęp za pomocą funkcji auth.token (lub request.auth.token) w wyrażeniach. Jeśli na przykład utworzysz roszczenie niestandardowe adminClaim, możesz uzyskać do niego dostęp za pomocą auth.token.adminClaim.

Co dalej?