Context
Sau khi set security rules trên Firebase:
match /vocab_entries/{entryId} {
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.userId;
}
mà gặp lỗi
7 PERMISSION_DENIED: Missing or insufficient permissions.] { code: 'permission-denied', customData: undefined, toString: [Function (anonymous)] }
Là do dùng server (route API) để kết nối với Firestore, trong khi sử dụng phương thức của client kết nối với Firestore -> Cần đổi lại phương thức
Firestore cung cấp 2 cách khác nhau để kết nối với db, từ client hoặc từ server, dùng đúng cách mới kết nối được
🧠 Tại sao bạn không thể lưu được dữ liệu từ API route (server)
🔥 Nguyên nhân chính:
auth.currentUser chỉ tồn tại trên client (trình duyệt) – nơi mà Firebase Auth SDK có thể quản lý session qua cookies hoặc localStorage.
Còn trên server (API route):
-
Firebase SDK không biết người dùng hiện tại là ai
-
auth.currentUserluôn lànull -
→ Vì vậy, khi gọi
addDoc(...)trong API route mà không cóauth, bạn gửi dữ liệu lên Firestore không có xác thực, hoặcuserIdcó thể không đúng -
→ Firestore Security Rules từ chối (
PERMISSION_DENIED)
✅ Vì sao client-side lại lưu được?
Trên client, Firebase Auth đã xác định chính xác currentUser, và auth.currentUser.uid được gửi kèm vào dữ liệu.
→ Security Rules có thể xác minh được:
request.auth.uid == request.resource.data.userId
🔐 Vậy lưu dữ liệu từ client có rủi ro bảo mật gì không?
❗ Có – nếu bạn không viết Security Rules đúng.
Firebase thiết kế để cho phép client trực tiếp giao tiếp với Firestore, nhưng bảo mật phụ thuộc hoàn toàn vào Security Rules.
✅ Ví dụ một rule an toàn (như bạn đang dùng):
match /vocab_entries/{entryId} {
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.userId;
}
🔒 Rule này bảo vệ được điều gì?
| Mối nguy | Có bị không? | Vì sao? |
|---|---|---|
| Người chưa đăng nhập gửi dữ liệu | ❌ | Vì request.auth != null kiểm tra đã đăng nhập |
Người A cố gắng gửi dữ liệu gán userId là của người B |
❌ | Rule kiểm tra request.auth.uid == request.resource.data.userId |
| Người khác đọc/xóa dữ liệu của bạn | ❌ | Rule kiểm tra auth.uid == resource.data.userId |
✅ Nếu rule đúng → lưu từ client là an toàn
❌ Nếu bạn để allow write: if true → toang toàn bộ
🛡️ Cách tăng cường bảo mật (không bắt buộc, tùy use case)
Nếu bạn muốn tối đa bảo mật:
-
👮♂️ Dùng API route + Firebase Admin SDK
-
Client gửi ID Token kèm request
-
Server dùng Admin SDK để
verifyIdToken() -
Sau đó server mới ghi data → giống mô hình backend truyền thống
-
→ Cách này khó hơn nhưng kiểm soát tốt hơn. Tuy nhiên, Firebase được thiết kế để client có thể trực tiếp giao tiếp với Firestore mà vẫn an toàn, nếu rules đúng.
✅ Tổng kết
| Điều | Giải thích |
|---|---|
| ❌ Không lưu được từ API route | Vì auth.currentUser trên server luôn null |
| ✅ Lưu từ client được | Vì client có auth.currentUser, gửi userId đúng |
| 🔐 Có rủi ro không? | Chỉ nếu bạn viết Security Rules sai |
| 🔒 Rule hiện tại của bạn có an toàn không? | Có – miễn là bạn giữ nguyên như hiện tại |
Nếu bạn muốn, mình có thể giúp bạn viết test case để kiểm tra rule giả lập các tình huống tấn công (ví dụ: dùng UID khác, chưa login...) — rất hữu ích trước khi mở app cho người dùng thật.