Firebase Data Connect memberikan keamanan sisi klien yang canggih dengan:
- Otorisasi klien seluler dan web
- Kontrol otorisasi tingkat kueri dan mutasi individual
- Pengesahan aplikasi dengan Firebase App Check.
Data Connect memperluas keamanan ini dengan:
- Otorisasi sisi server
- Keamanan pengguna project Firebase dan Cloud SQL dengan IAM.
Memberikan otorisasi kueri dan mutasi klien
Data Connect terintegrasi sepenuhnya dengan Firebase Authentication, sehingga Anda dapat menggunakan data lengkap tentang pengguna yang mengakses data Anda (autentikasi) dalam desain Anda untuk menentukan data yang dapat diakses pengguna tersebut (otorisasi).
Data Connect menyediakan direktif @auth
untuk kueri dan
mutasi yang memungkinkan Anda menetapkan tingkat autentikasi yang diperlukan untuk mengizinkan
operasi. Panduan ini
memperkenalkan direktif @auth
, dengan contoh.
Selain itu, Data Connect mendukung eksekusi kueri yang disematkan dalam mutasi, sehingga Anda dapat mengambil kriteria otorisasi tambahan yang telah disimpan dalam database, dan menggunakan kriteria tersebut dalam direktif @check
untuk memutuskan apakah mutasi yang melampirkan diizinkan. Untuk kasus otorisasi ini, direktif @redact
memungkinkan Anda mengontrol apakah hasil kueri ditampilkan ke klien dalam
protokol melalui kabel dan kueri tersemat dihilangkan dalam SDK yang dihasilkan. Temukan
pengantar untuk arahan ini, beserta contohnya.
Memahami perintah @auth
Anda dapat memparameterkan direktif @auth
untuk mengikuti salah satu dari beberapa tingkat akses preset
yang mencakup banyak skenario akses umum. Tingkatan ini berkisar dari
PUBLIC
(yang memungkinkan kueri dan mutasi dari semua klien tanpa
autentikasi apa pun) hingga NO_ACCESS
(yang tidak mengizinkan kueri dan
mutasi di luar lingkungan server dengan hak istimewa menggunakan Firebase Admin
SDK). Setiap level ini dikorelasikan dengan alur autentikasi yang disediakan oleh Firebase Authentication.
Tingkat | Definisi |
---|---|
PUBLIC |
Operasi dapat dijalankan oleh siapa saja dengan atau tanpa autentikasi. |
PUBLIC |
Operasi dapat dijalankan oleh siapa saja dengan atau tanpa autentikasi. |
USER_ANON |
Setiap pengguna yang teridentifikasi, termasuk pengguna yang telah login secara anonim dengan Firebase Authentication, diizinkan untuk melakukan kueri atau mutasi. |
USER |
Setiap pengguna yang telah login dengan Firebase Authentication diizinkan untuk melakukan kueri atau mutasi, kecuali pengguna login anonim. |
USER_EMAIL_VERIFIED |
Setiap pengguna yang telah login dengan Firebase Authentication dengan alamat email yang diverifikasi diizinkan untuk melakukan kueri atau mutasi. |
NO_ACCESS |
Operasi ini tidak dapat dijalankan di luar konteks Admin SDK. |
Dengan menggunakan tingkat akses preset ini sebagai titik awal, Anda dapat menentukan pemeriksaan otorisasi yang kompleks dan andal dalam direktif @auth
menggunakan filter where
dan ekspresi Common Expression Language (CEL) yang dievaluasi di server.
Gunakan direktif @auth
untuk menerapkan skenario otorisasi umum
Tingkat akses preset adalah titik awal untuk otorisasi.
Tingkat akses USER
adalah tingkat dasar yang paling berguna untuk memulai.
Akses yang sepenuhnya aman akan dibangun di tingkat USER
ditambah filter dan ekspresi
yang memeriksa atribut pengguna, atribut resource, peran, dan pemeriksaan lainnya. Tingkat
USER_ANON
dan USER_EMAIL_VERIFIED
adalah variasi pada kasus USER
.
Sintaksis ekspresi memungkinkan Anda mengevaluasi data menggunakan objek auth
yang merepresentasikan data autentikasi yang diteruskan dengan operasi, baik data standar dalam token auth maupun data kustom dalam token. Untuk mengetahui daftar kolom yang tersedia di objek auth
, lihat bagian referensi.
Tentu saja ada kasus penggunaan saat PUBLIC
adalah tingkat akses yang tepat untuk
dimulai. Sekali lagi, tingkat akses selalu menjadi titik awal, dan filter serta ekspresi tambahan diperlukan untuk keamanan yang kuat.
Panduan ini kini memberikan contoh cara membangun USER
dan PUBLIC
.
Contoh yang memotivasi
Contoh praktik terbaik berikut merujuk pada skema berikut untuk platform blog dengan konten tertentu yang dikunci di balik paket pembayaran.
Platform tersebut kemungkinan akan memodelkan Users
danPosts
.
type User @table(key: "uid") {
uid: String!
name: String
birthday: Date
createdAt: Timestamp! @default(expr: "request.time")
}
type Post @table {
author: User!
text: String!
# "one of 'draft', 'public', or 'pro'"
visibility: String! @default(value: "draft")
# "the time at which the post should be considered published. defaults to
# immediately"
publishedAt: Timestamp! @default(expr: "request.time")
createdAt: Timestamp! @def
ault(expr: "request.time")
updatedAt: Timestamp! @default(expr: "request.time")
}
Resource milik pengguna
Firebase merekomendasikan agar Anda menulis filter dan ekspresi yang menguji kepemilikan pengguna atas suatu resource, dalam kasus berikut, kepemilikan Posts
.
Dalam contoh berikut, data dari token autentikasi dibaca dan dibandingkan menggunakan
ekspresi. Pola umumnya adalah menggunakan ekspresi seperti where: {authorUid:
{eq_expr: "auth.uid"}}
untuk membandingkan authorUid
yang disimpan dengan auth.uid
(ID pengguna) yang diteruskan dalam token autentikasi.
Buat
Praktik otorisasi ini dimulai dengan menambahkan auth.uid
dari
token auth ke setiap Post
baru sebagai kolom authorUid
untuk memungkinkan perbandingan dalam
pengujian otorisasi berikutnya.
# Create a new post as the current user
mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
post_insert(data: {
# set the author's uid to the current user uid
authorUid_expr: "auth.uid"
text: $text
visibility: $visi
bility
})
}
Perbarui
Saat klien mencoba memperbarui Post
, Anda dapat menguji auth.uid
yang diteruskan
terhadap authorUid
yang disimpan.
# 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"
}
)
}
Hapus
Teknik yang sama digunakan untuk mengizinkan operasi penghapusan.
# 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 }
}
Daftar
# List all posts belonging to the current user
query ListMyPosts @auth(level: USER) {
posts(where: {
userUid: {eq_expr: "auth.uid"}
}) {
# See the fragment above
...DisplayPost
# also show visibility since it is user-controlled
visibil
ity
}
}
Dapatkan
# Get a post only if it belongs to the current user
query GetMyPost($id: UUID!) @auth(level: USER) {
post(key: {id: $id},
first: {where: {
id: {eq: $id}
authorUid: {eq_expr: "auth.uid"}}
}}, {
# See the fragment above
...DisplayPost
# also show visibility since it is user-controlled
visibil
ity
}
}
Memfilter Data
Sistem otorisasi Data Connect memungkinkan Anda menulis filter canggih yang dikombinasikan dengan tingkat akses preset seperti PUBLIC
serta dengan menggunakan data dari token autentikasi.
Sistem otorisasi juga memungkinkan Anda menggunakan ekspresi saja, tanpa tingkat akses dasar, seperti yang ditunjukkan dalam beberapa contoh berikut.
Memfilter menurut atribut resource
Di sini, otorisasi tidak didasarkan pada token autentikasi karena tingkat keamanan dasar ditetapkan ke PUBLIC
. Namun, kita dapat secara eksplisit menetapkan data dalam database kita sebagai
cocok untuk akses publik; asumsikan kita memiliki Post
data dalam database kita dengan
visibility
ditetapkan ke "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 fr
agment above
...DisplayPost
}
}
Memfilter menurut klaim pengguna
Di sini, asumsikan Anda telah menyiapkan klaim pengguna kustom yang meneruskan token autentikasi untuk mengidentifikasi pengguna dalam paket "pro" untuk aplikasi Anda, yang ditandai dengan kolom auth.token.plan
dalam token autentikasi. Ekspresi Anda dapat diuji terhadap kolom ini.
# 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
}
}
Filter menurut urutan + batas
Atau, Anda mungkin telah menetapkan visibility
dalam rekaman Post
untuk mengidentifikasi bahwa rekaman tersebut adalah konten yang tersedia untuk pengguna "pro", tetapi untuk pratinjau atau teaser listingan data, batasi lebih lanjut jumlah rekaman yang ditampilkan.
# Show 2 oldest Pro post as a preview
query ProTeaser @auth(level: USER) {
posts(
where: {
# show only pro posts
visibility: {eq: "pro"}
# that have already been published more than 30 days ago
publishedAt: {lt_time: {now: true, sub: {days: 30}}}
},
# order by publish time
orderBy: [{publishedAt: DESC}],
# only return two posts
limit: 2
) {
# See the fragment above
...DisplayP
ost
}
}
Filter menurut peran
Jika klaim kustom Anda menentukan peran admin
, Anda dapat menguji dan mengizinkan operasi yang sesuai.
# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
posts { ...Displa
yPost }
}
Tambahkan direktif @check
dan @redact
untuk mencari data otorisasi
Kasus penggunaan otorisasi umum melibatkan penyimpanan peran otorisasi kustom di database Anda, misalnya dalam tabel izin khusus, dan menggunakan peran tersebut untuk mengizinkan mutasi guna membuat, memperbarui, atau menghapus data.
Dengan menggunakan pencarian data otorisasi, Anda dapat membuat kueri untuk peran berdasarkan userID dan
menggunakan ekspresi CEL untuk memutuskan apakah mutasi diizinkan. Misalnya, Anda
mungkin ingin menulis mutasi UpdateMovieTitle
yang memungkinkan klien
yang berwenang memperbarui judul film.
Untuk pembahasan selanjutnya, asumsikan database aplikasi ulasan film menyimpan peran otorisasi dalam tabel 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!
}
Penggunaan dalam mutasi
Dalam contoh penerapan berikut, mutasi UpdateMovieTitle
mencakup kolom query
untuk mengambil data dari MoviePermission
, dan
direktif berikut untuk memastikan operasi aman dan andal:
- Direktif
@transaction
untuk memastikan semua kueri dan pemeriksaan otorisasi selesai atau gagal secara atomik. - Direktif
@redact
untuk menghilangkan hasil kueri dari respons, yang berarti pemeriksaan otorisasi kami dilakukan di server Data Connect, tetapi data sensitif tidak diekspos ke klien. Pasangan direktif
@check
untuk mengevaluasi logika otorisasi pada hasil kueri, seperti menguji bahwa userID tertentu memiliki peran yang sesuai untuk melakukan modifikasi.
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
})
}
Penggunaan dalam kueri
Pencarian data otorisasi juga berguna untuk membatasi kueri berdasarkan peran atau batasan lainnya.
Dalam contoh berikut, yang juga menggunakan skema MoviePermission
, kueri memeriksa apakah pemohon memiliki peran "admin" yang sesuai untuk melihat pengguna yang dapat mengedit 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
}
}
}
Antipola yang harus dihindari dalam otorisasi
Bagian sebelumnya membahas pola yang harus diikuti saat menggunakan direktif @auth
.
Anda juga harus mewaspadai antipola penting yang harus dihindari.
Hindari meneruskan ID atribut pengguna dan parameter token otorisasi dalam argumen kueri dan mutasi
Firebase Authentication adalah alat canggih untuk menyajikan alur autentikasi dan mengambil data autentikasi dengan aman seperti ID pengguna terdaftar dan berbagai kolom yang disimpan dalam token autentikasi.
Sebaiknya jangan meneruskan ID pengguna dan data token autentikasi dalam argumen kueri dan mutasi.
# 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
}
}
Hindari penggunaan tingkat akses USER
tanpa filter apa pun
Seperti yang dibahas beberapa kali dalam panduan, tingkat akses inti seperti USER
, USER_ANON
, USER_EMAIL_VERIFIED
adalah dasar dan titik awal untuk pemeriksaan otorisasi, yang akan ditingkatkan dengan filter dan ekspresi. Menggunakan tingkat ini tanpa filter atau ekspresi yang sesuai yang memeriksa pengguna mana yang melakukan permintaan pada dasarnya setara dengan menggunakan tingkat PUBLIC
.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
documents {
id
title
text
}
}
Hindari penggunaan tingkat akses PUBLIC
atau USER
untuk pembuatan prototipe
Untuk mempercepat pengembangan, Anda mungkin tergoda untuk menetapkan semua operasi ke tingkat akses PUBLIC
atau ke tingkat akses USER
tanpa peningkatan lebih lanjut untuk mengizinkan semua operasi dan memungkinkan Anda menguji kode dengan cepat.
Setelah Anda melakukan pembuatan prototipe awal dengan cara ini, mulailah beralih dari
NO_ACCESS
ke otorisasi siap produksi dengan tingkat PUBLIC
dan USER
.
Namun, jangan men-deploy-nya sebagai PUBLIC
atau USER
tanpa menambahkan logika tambahan seperti yang ditunjukkan dalam panduan ini.
# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
post: post_delete(
id: $id,
)
}
Hindari mendasarkan otorisasi pada alamat email yang belum diverifikasi
Memberi akses kepada pengguna di domain tertentu adalah cara yang tepat untuk membatasi akses. Namun, siapa pun dapat mengklaim kepemilikan email selama proses login. Pastikan Anda hanya memberikan akses ke alamat email yang telah diverifikasi melalui 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
})
}
Periksa juga auth.token.email_verified
mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email_veri&&fied auth.token.email.endsWith('@example.com')") {
post_insert(data: {
# set the author's uid to the current user uid
authorUid_expr: "auth.uid"
text: $text
visibility: $visibility
})
}
Mengaudit otorisasi dengan Firebase CLI
Seperti yang ditunjukkan sebelumnya, tingkat akses preset seperti PUBLIC
dan
USER
adalah titik awal untuk otorisasi yang kuat, dan
harus digunakan dengan pemeriksaan otorisasi berbasis filter dan ekspresi tambahan.
API ini tidak boleh digunakan sendiri tanpa mempertimbangkan kasus penggunaannya dengan cermat.
Data Connect membantu Anda mengaudit strategi otorisasi dengan menganalisis kode konektor saat Anda men-deploy ke server menggunakan firebase deploy
dari Firebase CLI. Anda dapat menggunakan audit ini untuk membantu meninjau codebase.
Saat Anda men-deploy konektor, CLI akan menampilkan penilaian untuk kode operasi yang ada, diubah, dan baru di konektor Anda.
Untuk operasi yang diubah dan baru, CLI akan mengeluarkan peringatan dan meminta konfirmasi saat Anda menggunakan tingkat akses tertentu dalam operasi baru, atau saat Anda mengubah operasi yang ada untuk menggunakan tingkat akses tersebut.
Peringatan dan perintah selalu muncul untuk:
PUBLIC
Selain itu, peringatan dan perintah muncul di tingkat akses berikut saat Anda
tidak menambahkannya dengan filter menggunakan auth.uid
:
USER
USER_ANON
USER_EMAIL_VERIFIED
Menyembunyikan peringatan operasi tidak aman dengan argumen @auth(insecureReason:)
Dalam banyak kasus, Anda akan menyimpulkan bahwa penggunaan tingkat akses PUBLIC
dan USER*
sangat tepat.
Jika konektor Anda berisi banyak operasi, Anda mungkin menginginkan output audit keamanan yang lebih jelas dan relevan yang menghilangkan operasi yang biasanya memicu peringatan, tetapi Anda tahu memiliki tingkat akses yang benar.
Anda dapat menyembunyikan peringatan untuk operasi tersebut dengan @auth(insecureReason:)
.
Contoh:
query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
{
items {
id name
}
}
Menggunakan Firebase App Check untuk pengesahan aplikasi
Autentikasi dan otorisasi adalah komponen penting dari keamanan Data Connect. Autentikasi dan otorisasi yang dikombinasikan dengan pengesahan aplikasi menghasilkan solusi keamanan yang sangat kuat.
Dengan pengesahan melalui Firebase App Check, perangkat yang menjalankan aplikasi Anda akan menggunakan penyedia pengesahan aplikasi atau perangkat yang menyatakan bahwa operasi Data Connect berasal dari aplikasi asli Anda dan permintaan berasal dari perangkat asli yang tidak dimodifikasi. Pengesahan ini dilampirkan pada setiap permintaan yang dibuat aplikasi Anda ke Data Connect.
Untuk mempelajari cara mengaktifkan App Check untuk Data Connect dan menyertakan SDK kliennya di aplikasi Anda, lihat ringkasan App Check.
Tingkat autentikasi untuk direktif @auth(level)
Tabel berikut mencantumkan semua tingkat akses standar dan padanannya dalam CEL. Tingkat autentikasi dicantumkan dari yang luas hingga yang sempit -- setiap tingkat mencakup semua pengguna yang cocok dengan tingkat berikutnya.
Tingkat | Definisi |
---|---|
PUBLIC |
Operasi dapat dijalankan oleh siapa saja dengan atau tanpa autentikasi.
Pertimbangan: Data dapat dibaca atau diubah oleh pengguna mana pun. Firebase merekomendasikan tingkat otorisasi ini untuk data yang dapat dilihat secara publik seperti listingan produk atau media. Lihat contoh dan alternatif praktik terbaik. Setara dengan @auth(expr: "true")
Filter dan ekspresi @auth tidak dapat digunakan bersamaan
dengan tingkat akses ini. Ekspresi tersebut akan gagal dengan error permintaan
buruk 400.
|
USER_ANON |
Setiap pengguna yang teridentifikasi, termasuk pengguna yang telah login secara anonim dengan Firebase Authentication, diizinkan untuk melakukan kueri atau mutasi.
Catatan: USER_ANON adalah superset dari USER .
Pertimbangan: Perhatikan bahwa Anda harus mendesain kueri dan mutasi dengan cermat untuk tingkat otorisasi ini. Level ini memungkinkan pengguna login secara anonim (login otomatis yang terikat hanya ke perangkat pengguna) dengan Authentication, dan dengan sendirinya tidak melakukan pemeriksaan lain, misalnya, apakah data milik pengguna. Lihat contoh dan alternatif praktik terbaik. Karena alur login anonim Authentication mengeluarkan uid , tingkat
USER_ANON setara dengan
@auth(expr: "auth.uid != nil")
|
USER |
Setiap pengguna yang telah login dengan Firebase Authentication diizinkan untuk
melakukan kueri atau mutasi, kecuali pengguna login anonim.
Pertimbangan: Perhatikan bahwa Anda harus mendesain kueri dan mutasi dengan cermat untuk tingkat otorisasi ini. Level ini hanya memeriksa apakah pengguna login dengan Authentication, dan tidak dengan sendirinya melakukan pemeriksaan lain, misalnya, apakah data milik pengguna. Lihat contoh dan alternatif praktik terbaik. Setara dengan @auth(expr: "auth.uid != nil &&
auth.token.firebase.sign_in_provider != 'anonymous'")"
|
USER_EMAIL_VERIFIED |
Setiap pengguna yang telah login dengan Firebase Authentication dengan alamat email yang diverifikasi diizinkan untuk melakukan kueri atau mutasi.
Pertimbangan: Karena verifikasi email dilakukan menggunakan Authentication, verifikasi ini didasarkan pada metode Authentication yang lebih andal, sehingga tingkat ini memberikan keamanan tambahan dibandingkan dengan USER atau
USER_ANON . Tingkat ini hanya memeriksa apakah pengguna login
dengan Authentication menggunakan email terverifikasi, dan tidak dengan sendirinya melakukan
pemeriksaan lain, misalnya, apakah data milik pengguna. Lihat
contoh dan alternatif praktik terbaik.
Setara dengan @auth(expr: "auth.uid != nil &&
auth.token.email_verified")" |
NO_ACCESS |
Operasi ini tidak dapat dijalankan di luar konteks Admin SDK.
Setara dengan @auth(expr: "false") |
Referensi CEL untuk @auth(expr)
Seperti yang ditunjukkan dalam contoh di tempat lain dalam panduan ini, Anda dapat dan harus menggunakan
ekspresi yang ditentukan dalam Common Expression Language (CEL) untuk mengontrol otorisasi
untuk Data Connect menggunakan direktif @auth(expr:)
dan @check
.
Bagian ini mencakup sintaksis CEL yang relevan untuk membuat ekspresi bagi direktif ini.
Informasi referensi lengkap untuk CEL disediakan dalam spesifikasi CEL.
Variabel pengujian yang diteruskan dalam kueri dan mutasi
Sintaksis @auth(expr)
memungkinkan Anda mengakses dan menguji variabel dari kueri dan mutasi.
Misalnya, Anda dapat menyertakan variabel operasi, seperti $status
, menggunakan
vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.statu
s)")
Data yang tersedia untuk ekspresi: request, response, this
Anda menggunakan data untuk:
- Evaluasi dengan ekspresi CEL dalam direktif
@auth(expr:)
dan@check(expr:)
- Penetapan menggunakan ekspresi server,
<field>_expr
.
Ekspresi CEL @auth(expr:)
dan @check(expr:)
dapat mengevaluasi hal berikut:
request.operationName
vars
(alias untukrequest.variables
)auth
(alias untukrequest.auth
)
Dalam mutasi, Anda dapat mengakses dan menetapkan konten:
response
(untuk memeriksa hasil parsial dalam logika multi-langkah)
Selain itu, ekspresi @check(expr:)
dapat mengevaluasi:
this
(nilai kolom saat ini)response
(untuk memeriksa hasil parsial dalam logika multi-langkah)
Binding request.operationName
Binding request.operarationName
menyimpan jenis operasi, baik kueri
atau mutasi.
Binding vars
(request.vars)
Binding vars
memungkinkan ekspresi Anda mengakses semua variabel yang diteruskan dalam kueri atau mutasi Anda.
Anda dapat menggunakan vars.<variablename>
dalam ekspresi sebagai alias untuk
request.variables.<variablename>
yang sepenuhnya memenuhi syarat:
# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.va
riables.v == 'hello'")
Binding auth
(request.auth)
Authentication mengidentifikasi pengguna yang meminta akses ke data Anda dan memberikan informasi tersebut sebagai binding yang dapat Anda bangun dalam ekspresi Anda.
Dalam filter dan ekspresi, Anda dapat menggunakan auth
sebagai alias untuk
request.auth
.
Binding auth berisi informasi berikut:
uid
: ID pengguna unik, yang ditetapkan untuk pengguna yang meminta.token
: Peta nilai yang dikumpulkan oleh Authentication.
Untuk mengetahui detail selengkapnya tentang konten auth.token
, lihat
Data dalam token auth
Binding response
Binding response
berisi data yang disusun oleh server sebagai
respons terhadap kueri atau mutasi saat data tersebut disusun.
Saat operasi berlanjut, dan setiap langkah berhasil diselesaikan,
response
berisi data respons dari langkah-langkah yang berhasil diselesaikan.
Binding response
disusun sesuai dengan bentuk operasi terkaitnya, termasuk kolom bertingkat (ganda) dan (jika berlaku) kueri sematan.
Perhatikan bahwa saat Anda mengakses data respons kueri sematan, kolom dapat berisi
jenis data apa pun, bergantung pada data yang diminta dalam kueri sematan; saat Anda
mengakses data yang ditampilkan oleh kolom mutasi seperti _insert
dan _delete
, data tersebut dapat
berisi kunci UUID, jumlah penghapusan, nilai null (lihat
referensi mutasi).
Contoh:
- Dalam mutasi yang berisi kueri sematan, pengikatan
response
berisi data pencarian diresponse.query.<fieldName>.<fieldName>....
, dalam hal ini,response.query.todoList
danresponse.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
}
}
}
- Dalam mutasi multi-langkah, misalnya dengan beberapa kolom
_insert
, bindingresponse
berisi data parsial diresponse.<fieldName>.<fieldName>....
, dalam hal ini,response.todoList_insert.id
.
mutation CreateTodoListWithFirstItem(
$listName: String!,
$itemContent: String!
) @transaction {
# Step 1
todoList_insert(data: {
id_expr: "uuidV4()",
name: $listName,
})
# Step 2:
todo_insert(data: {
listId_expr: "response.todoLis<t_insert.id" # -- Grab the newly generated ID from the partial response so far.
content: $
itemContent,
})
}
Binding this
.
Binding this
dievaluasi ke kolom yang dilampiri
direktif @check
. Dalam kasus dasar, Anda dapat mengevaluasi hasil kueri bernilai tunggal.
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
})
}
Jika kolom yang ditampilkan muncul beberapa kali karena ada ancestor yang berupa daftar, setiap kemunculan akan diuji dengan this
yang terikat ke setiap nilai.
Untuk jalur tertentu, jika ancestor adalah null
atau []
, kolom tidak akan
dijangkau dan evaluasi CEL akan dilewati untuk jalur tersebut. Dengan kata lain,
evaluasi hanya terjadi saat this
adalah null
atau non-null
, tetapi tidak pernah
undefined
.
Jika kolom itu sendiri adalah daftar atau objek, this
mengikuti struktur yang sama
(termasuk semua turunan yang dipilih jika berupa objek), seperti yang diilustrasikan dalam
contoh berikut.
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
})
}
Sintaksis ekspresi kompleks
Anda dapat menulis ekspresi yang lebih kompleks dengan menggabungkan operator &&
dan ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != n&&ull) (vars.username == '
;joe')")
Bagian berikut menjelaskan semua operator yang tersedia.
Operator dan prioritas operator
Gunakan tabel berikut sebagai referensi untuk operator dan prioritasnya yang sesuai.
Ekspresi arbitrer tertentu a
dan b
, kolom f
, dan indeks i
.
Operator | Deskripsi | Asosiativitas |
---|---|---|
a[i] a() a.f |
Akses indeks, panggilan, kolom | kiri ke kanan |
!a -a |
Negasi unary | kanan ke kiri |
a/b a%b a*b |
Operator multiplikatif | kiri ke kanan |
a+b a-b |
Operator aditif | kiri ke kanan |
a>b a>=b a<b a<=b |
Operator relasional | kiri ke kanan |
a in b |
Keberadaan dalam daftar atau peta | kiri ke kanan |
type(a) == t |
Perbandingan jenis, dengan t dapat berupa bool, int, float, angka, string, daftar, peta, stempel waktu, atau durasi |
kiri ke kanan |
a==b a!=b |
Operator perbandingan | kiri ke kanan |
a && b |
AND kondisional | kiri ke kanan |
a || b |
OR kondisional | kiri ke kanan |
a ? true_value : false_value |
Ekspresi ternary | kiri ke kanan |
Data dalam token autentikasi
Objek auth.token
dapat berisi nilai berikut:
Kolom | Deskripsi |
---|---|
email |
Alamat email yang terhubung dengan akun, jika ada. |
email_verified |
true jika pengguna telah memverifikasi bahwa mereka memiliki akses ke alamat email . Beberapa penyedia secara otomatis memverifikasi alamat email yang mereka miliki. |
phone_number |
Nomor telepon yang terkait dengan akun, jika ada. |
name |
Nama tampilan pengguna, jika ditetapkan. |
sub |
UID Firebase pengguna. UID ini bersifat unik dalam sebuah project. |
firebase.identities |
Kamus yang memuat semua identitas terkait akun pengguna ini. Kunci kamus dapat berupa salah satu dari berikut ini: email , phone , google.com , facebook.com , github.com , twitter.com . Nilai kamus adalah array ID unik untuk setiap penyedia identitas yang terkait dengan akun. Misalnya, auth.token.firebase.identities["google.com"][0] berisi ID pengguna Google pertama yang dikaitkan dengan akun. |
firebase.sign_in_provider |
Penyedia login yang digunakan untuk mendapatkan token ini. Dapat berupa salah satu string berikut: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant |
tenantId yang terkait dengan akun, jika ada. Contoh, tenant2-m6tyz |
Kolom tambahan di token ID JWT
Anda juga dapat mengakses kolom auth.token
berikut:
Klaim Token Kustom | ||
---|---|---|
alg |
Algoritme | "RS256" |
iss |
Penerbit | Alamat email akun layanan project Anda |
sub |
Subjek | Alamat email akun layanan project Anda |
aud |
Audience | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Waktu penerbitan | Waktu saat ini, dalam satuan detik sejak epoch UNIX |
exp |
Waktu habis masa berlaku |
Waktu saat token sudah tidak berlaku lagi, dalam satuan detik sejak epoch UNIX. Waktu ini bisa mencapai maksimum 3600 detik lebih lama daripada iat .
Catatan: ini hanya mengontrol kapan token kustom berhenti berlaku. Namun, setelah Anda memproses login menggunakan signInWithCustomToken() , pengguna akan tetap login di perangkatnya hingga validitas sesi berakhir atau pengguna tersebut logout.
|
<claims> (opsional) |
Klaim kustom opsional yang akan disertakan dalam token, yang dapat diakses melalui
auth.token (atau request.auth.token ) dalam
ekspresi. Misalnya, jika Anda membuat klaim kustom adminClaim , Anda dapat mengaksesnya dengan auth.token.adminClaim .
|
Apa langkah selanjutnya?
- Firebase Data Connect menyediakan Admin SDK untuk memungkinkan Anda melakukan kueri dan mutasi dari lingkungan istimewa.
- Pelajari keamanan IAM dalam panduan untuk mengelola layanan dan database.