Version info
Angular:
Angular CLI: 12.0.1
Node: 14.17.0
Package Manager: npm 7.13.0
OS: linux x64
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1200.1
@angular-devkit/build-angular 12.0.1
@angular-devkit/core 12.0.1
@angular-devkit/schematics 12.0.1
@angular/fire 6.1.5
@schematics/angular 12.0.1
rxjs 6.6.7
typescript 4.2.4
Firebase:
Some have reported that version 8.6.2 exhibits these errors. See comments below.
firebase-tools@9.11.0
firebase@8.6.1
AngularFire:
Other (e.g. Ionic/Cordova, Node, browser, operating system):
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal
Google Chrome: Version 90.0.4430.212 (Official Build) (64-bit)
How to reproduce these conditions
Steps to set up and reproduce
- Follow steps in AngularFire Quickstart
- Add authentication as described in 5. Getting started with Firebase Authentication
Sample data and security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if request.auth != null;
}
}
}
Sample data
"Leagues": [
{
"name": "Metro East Recreational Baseball League",
"location": "Collinsville, IL"
},
{
"name": "Southern Illinois Select Baseball League (SISBL)",
"location": "Freeburg, IL"
},
{
"name": "St Louis Baseball Forever League",
"location": "St Louis, MO"
},
{
"name": "Belleville Khoury League",
"location": "Belleville, IL"
},
{
"name": "Metro East Youth League of Belleville",
"location": "Belleville, IL"
},
{
"name": "Boulder Parks and Recreation Coed Softball League",
"location": "Boulder, CO"
}
]
Debug output
Errors in the JavaScript console
core.js:28028 Angular is running in development mode. Call enableProdMode() to enable production mode.
index.js:52 [WDS] Live Reloading enabled.
core.js:6456 ERROR FirebaseError: Missing or insufficient permissions.
at new e (http://localhost:35667/vendor.js:44309:19)
at http://localhost:35667/vendor.js:56105:18
at http://localhost:35667/vendor.js:56106:10
at e.onMessage (http://localhost:35667/vendor.js:56127:6)
at http://localhost:35667/vendor.js:56018:18
at http://localhost:35667/vendor.js:56056:29
at http://localhost:35667/vendor.js:61872:25
at ZoneDelegate.invoke (http://localhost:35667/polyfills.js:381:26)
at Zone.run (http://localhost:35667/polyfills.js:143:43)
at http://localhost:35667/polyfills.js:1285:36
defaultErrorLogger @ core.js:6456
Expected behavior
If the rules are changed to allow unauthenticated reads, the output should look like this:
- Metro East Recreational Baseball League — Collinsville, IL
- Southern Illinois Select Baseball League (SISBL) — Freeburg, IL
- St Louis Baseball Forever League — St Louis, MO
- Belleville Khoury League — Belleville, IL
- Metro East Youth League of Belleville — Belleville, IL
- Boulder Parks and Recreation Coed Softball League — Boulder, CO
Actual behavior
- The application emits none of the items from the database.
- Error is emitted in the browser console.
- Multiple browser refresh will eventually (after a few seconds) return the correct data, but a subsequent browser refresh results in the same error.
While experimenting with Firebase and specifically Cloud Firestore I reached the point where I had to control who can read and write data to my collections. Since the application I am building is a client-side application only, the Firebase way to restrict access to it is through Firebase Authentication and Cloud Firestore Security Rules.
For the purposes of this blog post, let’s assume that I have 1 main collection called flats
which includes a subcollection called items
.
After reading through the documentation, testing some rules, updating my collections structure, I ended up with the following rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /flats/{flatId} {
allow create: if request.auth != null;
allow read, update, delete: if request.auth != null
&& request.auth.uid in resource.data.idsOfUsersWithAccess;
match /items/{itemId} {
allow create: if request.auth != null;
allow read, update, delete: if request.auth != null
&& request.auth.uid in resource.data.idsOfFlatmatesThatShareThis;
}
}
}
}
Enter fullscreen mode
Exit fullscreen mode
What the above rules basically say is that any authenticated user (request.auth != null
) is allow
ed to create
a flat, but they are only allow
ed to read
, update
or delete
a flat if their unique ID (request.auth.uid
) is included in the flat’s property called idsOfUsersWithAccess
.
Now, since the items
collection is a subcollection of the flats
one, I created a nested rule which extends the previous one (read more about rules for hierarchical data in the docs). The rule says that only authenticated users are allow
ed to create
items but in order to read
, update
or delete
an item the user’s unique ID needs to be included in the idsOfFlatmatesThatShareThis
property of the flat.
For some reason however I could not fetch the items of the flat in my app and I was getting the following error:
Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
Enter fullscreen mode
Exit fullscreen mode
After quite a bit of digging into my code and the Firebase docs I found the problem. My code to fetch the items of a flat is the following:
firestore.collection('flats').doc(flat.id)
.collection('items').get()
Enter fullscreen mode
Exit fullscreen mode
My simple brain (I like to call him Brian) had this thought:
If the authenticated user queries all flat items, the result will surely only include the ones they have access too, based on the security rules that I created.
WRONG.
The documentation on writing conditions for Cloud Firestore Security Rules clearly says:
Rules are not filters. You cannot write a query for all the documents in a collection and expect Cloud Firestore to return only the documents that the current client has permission to access.
Oopsie!
The problem was not in my rules. It was in my query. Adding a where
clause to match the rule fixed the problem and I could now load the intented items:
firestore.collection('flats').doc(flat.id)
.collection('items')
.where('idsOfFlatmatesThatShareThis', 'array-contains', state.user.id)
.get()
Enter fullscreen mode
Exit fullscreen mode
What did I learn?
To spend a bit more time reading the docs before diving into coding! Or at least skimming through all of them quickly
PS: I would totally recommend the video series of Get to know Cloud Firestore on Youtube.
Technical Problem Cluster First Answered On
January 23, 2021
Popularity
10/10
Helpfulness
6/10
Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
Popularity
10/10 Helpfulness
6/10
Language
whatever
Contributed on Jan 23 2021
Dayanaohhnana
160 Answers Avg Quality 8/10
[Unhandled promise rejection: FirebaseError: Missing or insufficient permissions.]
Popularity
10/10 Helpfulness
4/10
Language
whatever
Contributed on Dec 21 2021
Tamer Jarrar
57 Answers Avg Quality 8/10
FirebaseError: Missing or insufficient permissions.
Popularity
9/10 Helpfulness
3/10
Language
whatever
Contributed on Aug 13 2022
Shehroze Ali
18 Answers Avg Quality 8/10
Issue
I’m attempting to allow each user read and write their own data using firestore, but I’m getting an insufficient permissions error. I’m not sure why.
I have these rules in place for my firestore…
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{uid} {
allow create: if request.auth != null;
allow read, write, update, delete: if request.auth != null && request.auth.uid == uid;
}
}
}
In my project, I have my service that uses the following function to push the data to angular firebase (yes, it’s pretty lengthy)…
constructor(private afs: AngularFirestore){}
addToOrders(artist: string, formInput: AlbumInput) {
const currentUser = this.authService.currentUser; // uses a getter function to obtain the current user
const trackingUrl = 'https://tools.usps.com/go/TrackConfirmAction_input?strOrigTrackNum=';
const newOrder: Order = {
artistName: artist,
album: formInput.selectedAlbum.name,
image: formInput.selectedAlbum.images[0].url,
orderType: formInput.orderType,
trackingUrl: trackingUrl,
variant: formInput.variant
}
if (formInput.orderType === "shipped") {
newOrder.trackingNum = formInput.trackingNum;
return of(this.afs.doc(`users/${currentUser.uid}`).collection('shipped').add(newOrder))
.subscribe({
next: (() => {
this.albumAdded$.next(true);
}),
error: (() => {
this.albumAdded$.next(false);
})
});
} else {
newOrder.date = formInput.date;
return of(this.afs.doc(`users/${currentUser.uid}`).collection('preordered').add(newOrder))
.subscribe({
next: (() => {
this.albumAdded$.next(true);
}),
error: (() => {
this.albumAdded$.next(false);
})
});
}
}
Is there anything I’m missing in this pattern that would cause such an error?
If I change the rules to users/${user=**}, it does successfully store the data into the users subcollections, but now I can’t sign in normally (for some reason, I can sign up despite the methods being nearly identical). Here is my sign in…
signIn(signInForm: SignInForm) {
return this.afAuth.signInWithEmailAndPassword(signInForm.email, signInForm.password)
.then((result) => {
this.isUserData.next(true);
this.setUserData(result.user!)
.then(() => {
this.router.navigateByUrl("/home");
});
}).catch(error => {
this.errorModal(error); // Modal Generic launches to inform the user
});
}
set user data…
setUserData(user: User) {
const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);
const userData: User = {
uid: user.uid,
email: user.email,
displayName: user.displayName
}
return userRef.set(userData, {
merge: true
});
}
Solution
This rule:
match /users/{uid} {
Allows a user to read their own profile document. It does not allow them to read subcollections under there, which is what you do in this code:
of(this.afs.doc(`users/${currentUser.uid}`).collection('shipped').add(newOrder))
to allow a user to also read all subcollections of their profile document, use a recursive wildcard (**
):
match /users/{uid=**} {
Answered By – Frank van Puffelen
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0
# #javascript #reactjs #firebase
Вопрос:
Я пытаюсь внедрить платежную систему stripe с помощью своего приложения react, в котором я использую firebase для хранения данных пользователей. Я перенял проект у другого разработчика, но столкнулся с проблемами, пытаясь получить идентификатор пользователя для объединения информации.
Я получаю ошибку Uncaught (in promise) FirebaseError: Missing or insufficient permissions.
при запуске моего checkout.js страница… вот что у меня есть:
user.js
class UserStripe {
constructor({ db, auth, firestoreEnvironment }) {
this.db = db
this.auth = auth
this.collections = new StripeCollectionUtil({ db, firestoreEnvironment, auth })
this.env = firestoreEnvironment
}
doCreateUser(uid, email, name, companyName) {
if (!companyName) companyName = null
return this.collections.getCollectionFor('users').doc(uid).set({
email,
name,
companyName,
areasAndTasks: JSON.stringify(defaultAreasAndTasks),
environment: this.env
})
}
getUserProfile() {
let uid = this.auth.currentUser.uid
return this.collections.getCollectionFor('users').doc(uid).get().then(doc => doc.data())
}
getAreasAndTasks() {
return this.getUserProfile().then((userProfile) => JSON.parse(userProfile.areasAndTasks))
}
}
module.exports = UserStripe
и это мое checkout.js
import firebase from 'firebase';
import getStripe from './stripe';
const firestore = firebase.firestore();
export async function createCheckoutSession(){
// without uid
// return firestore.collection()
const checkoutSessionRef = await firestore.collection('customers').doc().collection('checkout_sessions').add(
{price : 'price_1IGEdTKDPaWWeL1ymwWH5Ubb',
success_url : window.location.origin,
cancel_url: window.location.origin,
}
);
checkoutSessionRef.onSnapshot(async (snap) => {
const {sessionid} = snap.data();
if (sessionid) {
const stripe = await getStripe()
stripe.redirectToCheckout({sessionid})
}
});
}
как я могу импортировать uid на страницу оформления заказа, чтобы не получить эту ошибку?
Комментарии:
1. я бы рекомендовал заглянуть в правила firestore вашего проекта, там есть правило, запрещающее доступ на чтение/запись к одному из документов, к которым вы пытаетесь получить доступ в приведенных выше кодах