Firebase Data Connect cung cấp tính năng bảo mật phía máy khách mạnh mẽ với:
- Uỷ quyền cho ứng dụng di động và ứng dụng web
- Các chế độ kiểm soát uỷ quyền riêng lẻ ở cấp truy vấn và cấp đột biến
- Chứng thực ứng dụng bằng Firebase App Check.
Data Connect mở rộng tính năng bảo mật này bằng cách:
- Uỷ quyền phía máy chủ
- Bảo mật dự án Firebase và người dùng Cloud SQL bằng IAM.
Cho phép các truy vấn và đột biến của ứng dụng
Data Connect được tích hợp đầy đủ với Firebase Authentication, vì vậy, bạn có thể sử dụng dữ liệu phong phú về những người dùng đang truy cập vào dữ liệu của bạn (xác thực) trong thiết kế của bạn về những dữ liệu mà những người dùng đó có thể truy cập (uỷ quyền).
Data Connect cung cấp chỉ thị @auth
cho các truy vấn và đột biến, cho phép bạn đặt cấp độ xác thực cần thiết để uỷ quyền cho thao tác. Hướng dẫn này giới thiệu chỉ thị @auth
, kèm theo ví dụ.
Ngoài ra, Data Connect hỗ trợ việc thực thi các truy vấn được nhúng trong các đột biến, vì vậy, bạn có thể truy xuất các tiêu chí uỷ quyền bổ sung mà bạn đã lưu trữ trong cơ sở dữ liệu và sử dụng các tiêu chí đó trong chỉ thị @check
để quyết định xem các đột biến kèm theo có được uỷ quyền hay không. Đối với trường hợp uỷ quyền này, chỉ thị @redact
cho phép bạn kiểm soát việc kết quả truy vấn có được trả về cho các ứng dụng trong giao thức truyền dữ liệu và truy vấn nhúng bị bỏ qua trong các SDK đã tạo hay không. Tìm giới thiệu về các chỉ thị này, kèm theo ví dụ.
Tìm hiểu chỉ thị @auth
Bạn có thể tham số hoá chỉ thị @auth
để tuân theo một trong số các cấp truy cập đặt sẵn nhằm bao gồm nhiều trường hợp truy cập phổ biến. Các cấp độ này có phạm vi từ PUBLIC
(cho phép truy vấn và đột biến từ tất cả các máy khách mà không cần xác thực dưới bất kỳ hình thức nào) đến NO_ACCESS
(không cho phép truy vấn và đột biến bên ngoài các môi trường máy chủ đặc quyền bằng Firebase Admin SDK). Mỗi cấp độ này tương quan với các luồng xác thực do Firebase Authentication cung cấp.
Cấp độ | Định nghĩa |
---|---|
PUBLIC |
Bất kỳ ai có hoặc không có thông tin xác thực đều có thể thực hiện thao tác này. |
PUBLIC |
Bất kỳ ai có hoặc không có thông tin xác thực đều có thể thực hiện thao tác này. |
USER_ANON |
Mọi người dùng được xác định, kể cả những người đã đăng nhập ẩn danh bằng Firebase Authentication, đều được phép thực hiện truy vấn hoặc đột biến. |
USER |
Mọi người dùng đã đăng nhập bằng Firebase Authentication đều được phép thực hiện truy vấn hoặc đột biến, ngoại trừ người dùng đăng nhập ẩn danh. |
USER_EMAIL_VERIFIED |
Bất kỳ người dùng nào đã đăng nhập bằng Firebase Authentication có địa chỉ email đã xác minh đều được phép thực hiện truy vấn hoặc đột biến. |
NO_ACCESS |
Bạn không thể thực hiện thao tác này bên ngoài ngữ cảnh Admin SDK. |
Khi sử dụng các cấp truy cập đặt sẵn này làm điểm bắt đầu, bạn có thể xác định các quy trình kiểm tra uỷ quyền phức tạp và mạnh mẽ trong chỉ thị @auth
bằng cách sử dụng các bộ lọc where
và các biểu thức Ngôn ngữ biểu thức chung (CEL) được đánh giá trên máy chủ.
Sử dụng chỉ thị @auth
để triển khai các trường hợp uỷ quyền phổ biến
Các cấp truy cập đặt sẵn là điểm bắt đầu cho việc uỷ quyền.
Cấp truy cập USER
là cấp cơ bản hữu ích nhất để bắt đầu.
Quyền truy cập hoàn toàn an toàn sẽ dựa trên cấp USER
, cộng với các bộ lọc và biểu thức kiểm tra thuộc tính người dùng, thuộc tính tài nguyên, vai trò và các hoạt động kiểm tra khác. Các cấp USER_ANON
và USER_EMAIL_VERIFIED
là các biến thể của trường hợp USER
.
Cú pháp biểu thức cho phép bạn đánh giá dữ liệu bằng cách sử dụng đối tượng auth
đại diện cho dữ liệu xác thực được truyền bằng các thao tác, cả dữ liệu tiêu chuẩn trong mã thông báo xác thực và dữ liệu tuỳ chỉnh trong mã thông báo. Để biết danh sách các trường có trong đối tượng auth
, hãy xem phần tài liệu tham khảo.
Tất nhiên, có những trường hợp sử dụng mà PUBLIC
là cấp truy cập phù hợp để bắt đầu. Một lần nữa, cấp độ truy cập luôn là điểm bắt đầu và bạn cần có thêm các bộ lọc và biểu thức để tăng cường bảo mật.
Hướng dẫn này hiện cung cấp ví dụ về cách xây dựng trên USER
và PUBLIC
.
Ví dụ có tính khích lệ
Các ví dụ sau đây về phương pháp hay nhất đề cập đến giản đồ sau đây cho một nền tảng blog có một số nội dung bị khoá sau một gói thanh toán.
Nền tảng như vậy có khả năng sẽ mô hình hoá Users
và 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")
}
Tài nguyên thuộc quyền sở hữu của người dùng
Firebase đề xuất bạn viết các bộ lọc và biểu thức kiểm tra quyền sở hữu tài nguyên của người dùng, trong các trường hợp sau, quyền sở hữu Posts
.
Trong các ví dụ sau, dữ liệu từ mã thông báo uỷ quyền sẽ được đọc và so sánh bằng cách sử dụng các biểu thức. Mẫu điển hình là sử dụng các biểu thức như where: {authorUid:
{eq_expr: "auth.uid"}}
để so sánh một authorUid
đã lưu trữ với auth.uid
(mã nhận dạng người dùng) được truyền trong mã thông báo xác thực.
Tạo
Phương pháp uỷ quyền này bắt đầu bằng cách thêm auth.uid
từ mã thông báo uỷ quyền vào mỗi Post
mới dưới dạng một trường authorUid
để cho phép so sánh trong các kiểm thử uỷ quyền tiếp theo.
# 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
})
}
Cập nhật
Khi một ứng dụng cố gắng cập nhật Post
, bạn có thể kiểm thử auth.uid
đã truyền đối với authorUid
đã lưu trữ.
# 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"
}
)
}
Xóa
Bạn có thể sử dụng cùng một kỹ thuật để uỷ quyền cho các thao tác xoá.
# 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 }
}
Danh sách
# 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
}
}
Lọc dữ liệu
Hệ thống uỷ quyền của Data Connect cho phép bạn viết các bộ lọc phức tạp kết hợp với các cấp truy cập đặt sẵn như PUBLIC
cũng như bằng cách sử dụng dữ liệu từ mã thông báo uỷ quyền.
Hệ thống uỷ quyền cũng cho phép bạn chỉ sử dụng các biểu thức mà không cần cấp độ truy cập cơ bản, như trong một số ví dụ sau.
Lọc theo thuộc tính tài nguyên
Ở đây, việc uỷ quyền không dựa trên mã thông báo uỷ quyền vì cấp độ bảo mật cơ bản được đặt thành PUBLIC
. Tuy nhiên, chúng ta có thể đặt các bản ghi trong cơ sở dữ liệu một cách rõ ràng là phù hợp cho quyền truy cập công khai; giả sử chúng ta có Post
bản ghi trong cơ sở dữ liệu với visibility
được đặt thành "công khai".
# 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
}
}
Lọc theo thông báo xác nhận quyền sở hữu của người dùng
Giả sử bạn đã thiết lập các khai báo tuỳ chỉnh về người dùng truyền trong mã thông báo uỷ quyền để xác định người dùng trong gói "chuyên nghiệp" cho ứng dụng của bạn, được gắn cờ bằng trường auth.token.plan
trong mã thông báo uỷ quyền. Biểu thức của bạn có thể kiểm tra trường này.
# 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
}
}
Lọc theo thứ tự + giới hạn
Hoặc một lần nữa, bạn có thể đã đặt visibility
trong các bản ghi Post
để xác định rằng đó là nội dung dành cho người dùng "chuyên nghiệp", nhưng đối với bản xem trước hoặc danh sách dữ liệu giới thiệu, hãy giới hạn thêm số lượng bản ghi được trả về.
# 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
}
}
Lọc theo vai trò
Nếu khai báo tuỳ chỉnh của bạn xác định một vai trò admin
, bạn có thể kiểm thử và uỷ quyền các thao tác cho phù hợp.
# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
posts { ...DisplayPost }
}
Thêm chỉ thị @check
và @redact
để tra cứu dữ liệu uỷ quyền
Một trường hợp sử dụng uỷ quyền phổ biến là lưu trữ các vai trò uỷ quyền tuỳ chỉnh trong cơ sở dữ liệu của bạn, chẳng hạn như trong một bảng quyền đặc biệt và sử dụng các vai trò đó để uỷ quyền cho các đột biến nhằm tạo, cập nhật hoặc xoá dữ liệu.
Khi sử dụng các lệnh tra cứu dữ liệu uỷ quyền, bạn có thể truy vấn các vai trò dựa trên một mã nhận dạng người dùng và sử dụng các biểu thức CEL để quyết định xem thao tác biến đổi có được uỷ quyền hay không. Ví dụ: bạn có thể muốn viết một đột biến UpdateMovieTitle
cho phép một ứng dụng khách được uỷ quyền cập nhật tiêu đề phim.
Trong phần còn lại của cuộc thảo luận này, hãy giả sử cơ sở dữ liệu của ứng dụng đánh giá phim lưu trữ một vai trò uỷ quyền trong bảng 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!
}
Sử dụng trong đột biến
Trong quá trình triển khai ví dụ sau, đột biến UpdateMovieTitle
bao gồm trường query
để truy xuất dữ liệu từ MoviePermission
và các chỉ thị sau để đảm bảo hoạt động này an toàn và mạnh mẽ:
- Chỉ thị
@transaction
để đảm bảo tất cả các truy vấn và quy trình kiểm tra uỷ quyền đều được hoàn tất hoặc không thành công một cách tự động. - Chỉ thị
@redact
để bỏ qua kết quả truy vấn trong phản hồi, nghĩa là chế độ kiểm tra uỷ quyền của chúng tôi được thực hiện trên máy chủ Data Connect nhưng dữ liệu nhạy cảm không được hiển thị cho máy khách. Một cặp chỉ thị
@check
để đánh giá logic uỷ quyền trên kết quả truy vấn, chẳng hạn như kiểm thử rằng một userID nhất định có vai trò phù hợp để thực hiện các sửa đổi.
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
})
}
Sử dụng trong truy vấn
Việc tra cứu dữ liệu uỷ quyền cũng hữu ích để hạn chế các truy vấn dựa trên vai trò hoặc các hạn chế khác.
Trong ví dụ sau đây (cũng sử dụng giản đồ MoviePermission
), truy vấn sẽ kiểm tra xem người yêu cầu có vai trò "quản trị viên" phù hợp để xem những người dùng có thể chỉnh sửa một bộ phim hay không.
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
}
}
}
Các mẫu chống đối cần tránh trong quá trình uỷ quyền
Phần trước đề cập đến các mẫu cần tuân theo khi sử dụng chỉ thị @auth
.
Bạn cũng nên biết những phản mẫu quan trọng cần tránh.
Tránh truyền mã nhận dạng thuộc tính người dùng và các tham số mã thông báo uỷ quyền trong các đối số truy vấn và đột biến
Firebase Authentication là một công cụ mạnh mẽ để trình bày các luồng xác thực và thu thập dữ liệu xác thực một cách an toàn, chẳng hạn như mã nhận dạng người dùng đã đăng ký và nhiều trường được lưu trữ trong mã thông báo xác thực.
Bạn không nên truyền mã nhận dạng người dùng và dữ liệu mã thông báo uỷ quyền trong các đối số truy vấn và đột biến.
# 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
}
}
Tránh sử dụng cấp truy cập USER
mà không có bộ lọc nào
Như đã thảo luận nhiều lần trong hướng dẫn, các cấp truy cập cốt lõi như USER
, USER_ANON
, USER_EMAIL_VERIFIED
là đường cơ sở và điểm bắt đầu cho các quy trình kiểm tra uỷ quyền, cần được tăng cường bằng các bộ lọc và biểu thức. Việc sử dụng các cấp này mà không có bộ lọc hoặc biểu thức tương ứng để kiểm tra xem người dùng nào đang thực hiện yêu cầu về cơ bản tương đương với việc sử dụng cấp PUBLIC
.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
documents {
id
title
text
}
}
Tránh sử dụng cấp truy cập PUBLIC
hoặc USER
để tạo mẫu
Để tăng tốc quá trình phát triển, bạn có thể muốn đặt tất cả các thao tác thành cấp truy cập PUBLIC
hoặc cấp truy cập USER
mà không cần cải tiến thêm để uỷ quyền tất cả các thao tác và cho phép bạn kiểm thử mã một cách nhanh chóng.
Khi bạn đã thực hiện việc tạo mẫu ban đầu theo cách này, hãy bắt đầu chuyển từ NO_ACCESS
sang uỷ quyền sẵn sàng cho sản xuất với các cấp PUBLIC
và USER
.
Tuy nhiên, đừng triển khai chúng dưới dạng PUBLIC
hoặc USER
mà không thêm logic bổ sung như trong hướng dẫn này.
# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
post: post_delete(
id: $id,
)
}
Tránh dựa vào địa chỉ email chưa được xác minh để uỷ quyền
Cấp quyền truy cập cho người dùng trên một miền cụ thể là một cách hiệu quả để hạn chế quyền truy cập. Tuy nhiên, bất kỳ ai cũng có thể khai báo rằng họ sở hữu một email trong quá trình đăng nhập. Đảm bảo rằng bạn chỉ cấp quyền truy cập cho những địa chỉ email đã được xác minh thông qua Xác thực Firebase.
# 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
})
}
Ngoài ra, hãy kiểm tra 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
})
}
Kiểm tra uỷ quyền bằng Giao diện dòng lệnh (CLI) của Firebase
Như đã đề cập trước đó, các cấp truy cập đặt sẵn như PUBLIC
và USER
là điểm bắt đầu cho việc uỷ quyền mạnh mẽ và nên được dùng cùng với các chế độ kiểm tra uỷ quyền dựa trên biểu thức và bộ lọc bổ sung.
Bạn không nên sử dụng các API này một cách độc lập mà không cân nhắc kỹ lưỡng trường hợp sử dụng.
Data Connect giúp bạn kiểm tra chiến lược uỷ quyền bằng cách phân tích mã trình kết nối khi bạn triển khai lên máy chủ bằng firebase deploy
từ CLI Firebase. Bạn có thể sử dụng quy trình kiểm tra này để xem xét cơ sở mã của mình.
Khi bạn triển khai các trình kết nối, CLI sẽ đưa ra các đánh giá về mã thao tác hiện có, đã sửa đổi và mới trong trình kết nối của bạn.
Đối với các thao tác đã sửa đổi và thao tác mới, CLI sẽ đưa ra cảnh báo và nhắc bạn xác nhận khi bạn sử dụng một số cấp truy cập nhất định trong các thao tác mới hoặc khi bạn sửa đổi các thao tác hiện có để sử dụng các cấp truy cập đó.
Cảnh báo và lời nhắc luôn xuất hiện trong các trường hợp sau:
PUBLIC
Ngoài ra, cảnh báo và lời nhắc sẽ xuất hiện ở các cấp truy cập sau đây khi bạn không tăng cường các cấp truy cập này bằng bộ lọc thông qua auth.uid
:
USER
USER_ANON
USER_EMAIL_VERIFIED
Chặn cảnh báo về thao tác không an toàn bằng đối số @auth(insecureReason:)
Trong nhiều trường hợp, bạn sẽ kết luận rằng việc sử dụng các cấp truy cập PUBLIC
và USER*
là hoàn toàn phù hợp.
Khi trình kết nối của bạn chứa nhiều thao tác, bạn có thể muốn đầu ra kiểm tra bảo mật rõ ràng và phù hợp hơn, loại bỏ những thao tác thường kích hoạt cảnh báo, nhưng bạn biết rằng mình có cấp truy cập chính xác.
Bạn có thể chặn cảnh báo cho các thao tác như vậy bằng @auth(insecureReason:)
.
Ví dụ:
query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
{
items {
id name
}
}
Sử dụng Firebase App Check để chứng thực ứng dụng
Xác thực và uỷ quyền là các thành phần quan trọng của bảo mật Data Connect. Xác thực và uỷ quyền kết hợp với chứng thực ứng dụng tạo nên một giải pháp bảo mật rất mạnh mẽ.
Với chứng thực thông qua Firebase App Check, các thiết bị chạy ứng dụng của bạn sẽ sử dụng một trình cung cấp chứng thực ứng dụng hoặc thiết bị để chứng thực rằng các thao tác Data Connect bắt nguồn từ ứng dụng chính thống của bạn và các yêu cầu bắt nguồn từ một thiết bị chính thống, không bị can thiệp. Chứng thực này được đính kèm vào mọi yêu cầu mà ứng dụng của bạn gửi đến Data Connect.
Để tìm hiểu cách bật App Check cho Data Connect và đưa SDK ứng dụng của App Check vào ứng dụng, hãy xem phần App Checktổng quan.
Các cấp xác thực cho chỉ thị @auth(level)
Bảng sau đây liệt kê tất cả các cấp truy cập tiêu chuẩn và các cấp truy cập tương đương trong CEL. Các cấp xác thực được liệt kê từ phạm vi rộng đến phạm vi hẹp – mỗi cấp bao gồm tất cả người dùng phù hợp với các cấp sau.
Cấp độ | Định nghĩa |
---|---|
PUBLIC |
Bất kỳ ai có hoặc không có thông tin xác thực đều có thể thực hiện thao tác này.
Lưu ý: Mọi người dùng đều có thể đọc hoặc sửa đổi dữ liệu. Firebase đề xuất cấp uỷ quyền này cho dữ liệu có thể duyệt công khai, chẳng hạn như trang thông tin sản phẩm hoặc nội dung nghe nhìn. Xem các ví dụ về phương pháp hay nhất và các lựa chọn thay thế. Tương đương với @auth(expr: "true")
Không thể kết hợp các bộ lọc và biểu thức @auth với cấp truy cập này. Mọi biểu thức như vậy sẽ không thành công và báo lỗi 400 yêu cầu không hợp lệ.
|
USER_ANON |
Mọi người dùng được xác định, kể cả những người đã đăng nhập ẩn danh bằng Firebase Authentication, đều được phép thực hiện truy vấn hoặc đột biến.
Lưu ý: USER_ANON là một siêu tập hợp của USER .
Lưu ý: Xin lưu ý rằng bạn phải thiết kế cẩn thận các truy vấn và đột biến cho cấp uỷ quyền này. Cấp độ này cho phép người dùng đăng nhập ẩn danh (tính năng tự động đăng nhập chỉ liên kết với thiết bị của người dùng) bằng Authentication và không tự thực hiện các bước kiểm tra khác, chẳng hạn như liệu dữ liệu có thuộc về người dùng hay không. Xem các ví dụ về phương pháp hay nhất và các lựa chọn thay thế. Vì quy trình đăng nhập ẩn danh Authentication sẽ phát hành một uid , nên cấp USER_ANON tương đương với @auth(expr: "auth.uid != nil")
|
USER |
Mọi người dùng đã đăng nhập bằng Firebase Authentication đều được phép thực hiện truy vấn hoặc đột biến, ngoại trừ người dùng đăng nhập ẩn danh.
Lưu ý: Xin lưu ý rằng bạn phải thiết kế cẩn thận các truy vấn và đột biến cho cấp uỷ quyền này. Cấp độ này chỉ kiểm tra xem người dùng có đăng nhập bằng Authentication hay không và không tự thực hiện các bước kiểm tra khác, chẳng hạn như liệu dữ liệu có thuộc về người dùng hay không. Xem các ví dụ về phương pháp hay nhất và các lựa chọn thay thế. Tương đương với @auth(expr: "auth.uid != nil &&
auth.token.firebase.sign_in_provider != 'anonymous'")"
|
USER_EMAIL_VERIFIED |
Bất kỳ người dùng nào đã đăng nhập bằng Firebase Authentication có địa chỉ email đã xác minh đều được phép thực hiện truy vấn hoặc đột biến.
Lưu ý: Vì quy trình xác minh email được thực hiện bằng Authentication, nên quy trình này dựa trên phương thức Authentication mạnh mẽ hơn. Do đó, cấp độ này mang lại khả năng bảo mật bổ sung so với USER hoặc USER_ANON . Cấp này chỉ kiểm tra xem người dùng có đăng nhập bằng Authentication bằng email đã xác minh hay không và không tự thực hiện các bước kiểm tra khác, chẳng hạn như liệu dữ liệu có thuộc về người dùng hay không. Xem các ví dụ về phương pháp hay nhất và các lựa chọn thay thế.
Tương đương với @auth(expr: "auth.uid != nil &&
auth.token.email_verified")" |
NO_ACCESS |
Bạn không thể thực hiện thao tác này bên ngoài ngữ cảnh Admin SDK.
Tương đương với @auth(expr: "false") |
Tài liệu tham khảo về CEL cho @auth(expr)
Như trong các ví dụ ở những nơi khác trong hướng dẫn này, bạn có thể và nên sử dụng các biểu thức được xác định trong Ngôn ngữ biểu thức thông thường (CEL) để kiểm soát việc uỷ quyền cho Data Connect bằng cách sử dụng các chỉ thị @auth(expr:)
và @check
.
Phần này trình bày cú pháp CEL có liên quan đến việc tạo biểu thức cho các chỉ thị này.
Thông tin tham khảo đầy đủ về CEL được cung cấp trong quy cách CEL.
Kiểm thử các biến được truyền trong truy vấn và đột biến
Cú pháp @auth(expr)
cho phép bạn truy cập và kiểm thử các biến từ truy vấn và đột biến.
Ví dụ: bạn có thể thêm một biến thao tác, chẳng hạn như $status
, bằng cách sử dụng vars.status
.
mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")
Dữ liệu có sẵn cho các biểu thức: request, response, this
Bạn sử dụng dữ liệu cho:
- Đánh giá bằng các biểu thức CEL trong chỉ thị
@auth(expr:)
và@check(expr:)
- Chỉ định bằng cách sử dụng biểu thức máy chủ,
<field>_expr
.
Cả biểu thức @auth(expr:)
và @check(expr:)
CEL đều có thể đánh giá những điều sau:
request.operationName
vars
(bí danh chorequest.variables
)auth
(bí danh chorequest.auth
)
Trong các đột biến, bạn có thể truy cập và chỉ định nội dung của:
response
(để kiểm tra kết quả một phần trong logic nhiều bước)
Ngoài ra, các biểu thức @check(expr:)
có thể đánh giá:
this
(giá trị của trường hiện tại)response
(để kiểm tra kết quả một phần trong logic nhiều bước)
Liên kết request.operationName
Liên kết request.operarationName
lưu trữ loại thao tác, có thể là truy vấn hoặc đột biến.
Liên kết vars
(request.vars)
Liên kết vars
cho phép các biểu thức của bạn truy cập vào tất cả các biến được truyền trong truy vấn hoặc đột biến.
Bạn có thể dùng vars.<variablename>
trong một biểu thức làm bí danh cho request.variables.<variablename>
đủ điều kiện:
# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")
Liên kết auth
(request.auth)
Authentication xác định những người dùng yêu cầu truy cập vào dữ liệu của bạn và cung cấp thông tin đó dưới dạng một liên kết mà bạn có thể tạo trong các biểu thức.
Trong bộ lọc và biểu thức, bạn có thể sử dụng auth
làm tên thay thế cho request.auth
.
Liên kết uỷ quyền chứa những thông tin sau:
uid
: Mã nhận dạng người dùng duy nhất, được chỉ định cho người dùng yêu cầu.token
: Bản đồ các giá trị do Authentication thu thập.
Để biết thêm thông tin về nội dung của auth.token
, hãy xem phần Dữ liệu trong mã thông báo uỷ quyền
Liên kết response
Liên kết response
chứa dữ liệu mà máy chủ đang tập hợp để phản hồi một truy vấn hoặc đột biến khi dữ liệu đó đang được tập hợp.
Khi thao tác diễn ra, khi mỗi bước hoàn tất thành công, response
sẽ chứa dữ liệu phản hồi từ các bước đã hoàn tất thành công.
Liên kết response
được cấu trúc theo hình dạng của thao tác được liên kết, bao gồm (nhiều) trường lồng nhau và (nếu có) các truy vấn được nhúng.
Xin lưu ý rằng khi bạn truy cập vào dữ liệu phản hồi truy vấn được nhúng, các trường có thể chứa mọi loại dữ liệu, tuỳ thuộc vào dữ liệu được yêu cầu trong truy vấn được nhúng; khi bạn truy cập vào dữ liệu do các trường đột biến như _insert
và _delete
trả về, các trường này có thể chứa khoá UUID, số lượt xoá, giá trị rỗng (xem tài liệu tham khảo về đột biến).
Ví dụ:
- Trong một đột biến chứa truy vấn được nhúng, liên kết
response
chứa dữ liệu tra cứu tạiresponse.query.<fieldName>.<fieldName>....
, trong trường hợp này làresponse.query.todoList
vàresponse.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
}
}
}
- Trong một đột biến nhiều bước, chẳng hạn như có nhiều trường
_insert
, liên kếtresponse
chứa dữ liệu một phần tạiresponse.<fieldName>.<fieldName>....
, trong trường hợp này là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,
})
}
Liên kết this
Liên kết this
đánh giá trường mà chỉ thị @check
được đính kèm. Trong trường hợp cơ bản, bạn có thể đánh giá kết quả truy vấn có một giá trị.
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
})
}
Nếu trường được trả về xuất hiện nhiều lần vì bất kỳ phần tử nào là một danh sách, thì mỗi lần xuất hiện sẽ được kiểm thử bằng this
được liên kết với từng giá trị.
Đối với bất kỳ đường dẫn nào, nếu một phần tử cấp trên là null
hoặc []
, thì trường đó sẽ không được truy cập và quá trình đánh giá CEL sẽ bị bỏ qua đối với đường dẫn đó. Nói cách khác, quá trình đánh giá chỉ diễn ra khi this
là null
hoặc không phải null
, nhưng không bao giờ là undefined
.
Khi chính trường này là một danh sách hoặc đối tượng, this
sẽ tuân theo cùng một cấu trúc (bao gồm cả tất cả các phần tử con được chọn trong trường hợp là đối tượng), như minh hoạ trong ví dụ sau.
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
})
}
Cú pháp biểu thức phức tạp
Bạn có thể viết các biểu thức phức tạp hơn bằng cách kết hợp với các toán tử &&
và ||
.
mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")
Phần sau đây mô tả tất cả các toán tử có sẵn.
Toán tử và mức độ ưu tiên của toán tử
Hãy tham khảo bảng sau đây để biết các toán tử và mức độ ưu tiên tương ứng của chúng.
Cho trước các biểu thức tuỳ ý a
và b
, một trường f
và một chỉ mục i
.
Toán tử | Mô tả | Tính kết hợp |
---|---|---|
a[i] a() a.f |
Chỉ mục, cuộc gọi, quyền truy cập vào trường | từ trái sang phải |
!a -a |
Phép phủ định một ngôi | phải sang trái |
a/b a%b a*b |
Toán tử nhân | từ trái sang phải |
a+b a-b |
Toán tử cộng | từ trái sang phải |
a>b a>=b a<b a<=b |
Toán tử quan hệ | từ trái sang phải |
a in b |
Sự tồn tại trong danh sách hoặc bản đồ | từ trái sang phải |
type(a) == t |
So sánh kiểu, trong đó t có thể là bool, int, float, number, string, list, map, timestamp hoặc duration |
từ trái sang phải |
a==b a!=b |
Toán tử so sánh | từ trái sang phải |
a && b |
Điều kiện VÀ | từ trái sang phải |
a || b |
Điều kiện HOẶC | từ trái sang phải |
a ? true_value : false_value |
Biểu thức bậc ba | từ trái sang phải |
Dữ liệu trong mã thông báo xác thực
Đối tượng auth.token
có thể chứa các giá trị sau:
Trường | Mô tả |
---|---|
email |
Địa chỉ email được liên kết với tài khoản (nếu có). |
email_verified |
true nếu người dùng đã xác minh rằng họ có quyền truy cập vào địa chỉ email . Một số nhà cung cấp tự động xác minh địa chỉ email mà họ sở hữu. |
phone_number |
Số điện thoại được liên kết với tài khoản (nếu có). |
name |
Tên hiển thị của người dùng (nếu được đặt). |
sub |
UID Firebase của người dùng. Giá trị này là duy nhất trong một dự án. |
firebase.identities |
Từ điển chứa tất cả danh tính được liên kết với tài khoản của người dùng này. Khoá của từ điển có thể là bất kỳ khoá nào sau đây: email , phone , google.com , facebook.com , github.com , twitter.com . Các giá trị của từ điển là mảng gồm các giá trị nhận dạng duy nhất cho mỗi nhà cung cấp dịch vụ danh tính được liên kết với tài khoản. Ví dụ: auth.token.firebase.identities["google.com"][0] chứa mã nhận dạng người dùng Google đầu tiên được liên kết với tài khoản. |
firebase.sign_in_provider |
Nhà cung cấp dịch vụ đăng nhập được dùng để lấy mã thông báo này. Có thể là một trong các chuỗi sau: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com . |
firebase.tenant |
tenantId được liên kết với tài khoản (nếu có). Ví dụ: tenant2-m6tyz |
Các trường bổ sung trong mã thông báo nhận dạng JWT
Bạn cũng có thể truy cập vào các trường auth.token
sau đây:
Xác nhận quyền sở hữu mã thông báo tuỳ chỉnh | ||
---|---|---|
alg |
Thuật toán | "RS256" |
iss |
Tổ chức phát hành | Địa chỉ email tài khoản dịch vụ của dự án |
sub |
Chủ đề | Địa chỉ email tài khoản dịch vụ của dự án |
aud |
Đối tượng | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat |
Thời gian phát hành | Thời gian hiện tại, tính bằng giây kể từ thời gian bắt đầu của hệ thống UNIX |
exp |
Thời gian hết hạn |
Thời gian (tính bằng giây kể từ thời gian bắt đầu của hệ thống UNIX) mà mã thông báo hết hạn. Thời gian này có thể muộn hơn tối đa 3.600 giây so với iat .
Lưu ý: chế độ này chỉ kiểm soát thời gian hết hạn của chính mã thông báo tuỳ chỉnh. Nhưng sau khi bạn đăng nhập cho người dùng bằng signInWithCustomToken() , họ sẽ vẫn đăng nhập vào thiết bị cho đến khi phiên của họ không hợp lệ hoặc người dùng đăng xuất.
|
<claims> (không bắt buộc) |
Các thông tin xác nhận tuỳ chỉnh không bắt buộc để đưa vào mã thông báo, có thể truy cập thông qua auth.token (hoặc request.auth.token ) trong các biểu thức. Ví dụ: nếu tạo một câu lệnh tuỳ chỉnh adminClaim , bạn có thể truy cập vào câu lệnh đó bằng auth.token.adminClaim .
|
Tiếp theo là gì?
- Firebase Data Connect cung cấp một Admin SDK để cho phép bạn thực hiện các truy vấn và đột biến từ các môi trường đặc quyền.
- Tìm hiểu về tính năng bảo mật IAM trong hướng dẫn quản lý các dịch vụ và cơ sở dữ liệu.