Jiren
Jiren

Reputation: 653

Firebase security rules insufficient permissions

I have the owner role in my firebase project and I am trying to secure my Firestore Database. I want my collections to be secured by default and only accessible if I make a rule for it. I created my database in production mode. However, for some reason even if I specify a rule that allows me to access a collection I receive a Missing or insufficient permissions error

For example when I register a user I also want to create my own user collection. registering the user works but creating my collection does not work.

register user query (works and adds a user to firebase authentication)

register(value) : Promise<any> {
    return new Promise<any>((resolve, reject) => {
        this.afAuth.createUserWithEmailAndPassword(value.email, value.password)
        .then(res => {
            resolve(res);
        }, err => {
            console.log(err)
            reject(err);
            })
    
    });
}

Create User collection query (CreateCurrentUserCollection does not work)

public async getCurrentUser() : Promise<any> 
    {
        return new Promise<any>((resolve, reject) => {
            let user = this.afAuth.onAuthStateChanged(function(user){
                if (user) {
                    resolve(user);
                } else {
                    reject('No user logged in');
                }
            });
        });
    }   

public CreateCurrentUserCollection(): void
{
  this.authService.getCurrentUser().then(res => {
        this.firestore.collection<User>("usersBackup")
            .add({
                uid: res.uid,
                username: res.email
            })
  });
}

Firebase rules

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
      
       match /users {
        allow read, write: if
          request.time < timestamp.date(2221, 7, 20);
       }
       
       match /usersBackup {
        allow read, write: if
          request.time < timestamp.date(2221, 7, 20);
       }
  }
}

if I add the following rule for example

match /{document=**} {
  allow read, write: if request.auth != null;

it does work. However then none of my other security rules work, where I want to prevent certain users from accessing certain documents. What am I missing or doing wrong? Please let me know if you need more information thank you in advance.

Update

As noted by Dharmaraj in his answer security rules must point to documents and not collection. This fixes my issue with one of my collection. However I also have a projects collection with an array field called projectMembers. I only want users with a uid inside the projectMembers array to have access to a project document. For some reason It is always giving me Missing or insufficient permissions error, even if the projects collection is empty.

query

    public getProjectsOfUser(userUid: string) : Observable<IProject[]> {
        return this.firestore.collection<IProject>("projects", ref => 
            ref.where("projectMembers", "array-contains", userUid))
           .valueChanges({ idField: 'id'});
}

Firebase Rules

    rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
      
       match /users/{userID} {
        allow read, write: if isSignedIn() && request.auth.uid == userID
       }
       
       match /usersBackup/{document=**} {
        allow read, write: if
          request.time < timestamp.date(2221, 7, 20);
       }
        
      match /projects/{projectId} {
        allow read, write: if 
            isProjectMember(projectId, request.auth.uid);
      }
      
      //functions
    function isSignedIn()
    {
        return request.auth != null;
    }
    
    function isProjectMember(projectId, userId) {
    return userId in get(/databases/$(database)/documents/projects/$(projectId)).data.projectMembers;
    }
    
  }
}

Database Structure enter image description here

Firebase Playground enter image description here

JWT token send to Firebase by Angular enter image description here

Upvotes: 3

Views: 366

Answers (1)

Dharmaraj
Dharmaraj

Reputation: 50830

As mentioned in the documentation,

All match statements should point to documents, not collections. A match statement can point to a specific document, as in match /cities/SF or use wildcards to point to any document in the specified path, as in match /cities/{city}.

If you want users to edit their own document only you should try:

match /users/{userID} {
  allow read, write: request.auth != null && request.auth.uid == userID;
}

If not, then you can try adding a recursive wildcard so the rule will be applied for all the documents in that collection:

match /users/{document=**} {
  allow read, write: request.time < timestamp.date(2221, 7, 20);
}

Do note that anyone can write to any document if you use the recursive wildcard.

Update: To check if an UID exists in the array.

allow read, write: if request.auth != null && request.auth.uid in resource.data.projectMembers

Upvotes: 2

Related Questions