2
votes

I am lost with the Firestore Rules.

I want authenticated users to be able to read their own submits, but I keep receiving insufficient permissions. I am writing the userId into each submit.

// add submit to submits collection in firestore
    db.collection('submits').add({
      user: this.user,
      number: this.number,
      timestamp: moment.utc(this.timestamp).format(),
      usage: this.usage
    })

Here I check which user is logged in and fetch the user his submits

 let ref = db.collection('users')

// get current user
ref.where('user_id', '==', firebase.auth().currentUser.uid).get()
  .then(snapshot => {
    snapshot.forEach(doc => {
      this.user = doc.data()
      this.user = doc.data().user_id
    })
  })
  .then(() => {
    // fetch the user previous submits from the firestore
    db.collection('submits').where('user', '==', this.user).get()
      .then(snapshot => {
        // console.log(snapshot)
        snapshot.forEach(doc => {
          let submit = doc.data()
          submit.id = doc.id
          submit.timestamp = moment(doc.data().timestamp).format('lll')
          this.previousSubmits.push(submit)
        })
      })
  })
  }

These are my firestore rules

service cloud.firestore {
match /databases/{database}/documents {

// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
  allow read, update, delete: if request.auth.uid == userId;
  allow create: if request.auth.uid != null;
}

// check if the user is owner of submits he is requesting
match /submits/{document=**} {
     allow read: if resource.data.user == request.auth.uid;
         allow write: if request.auth.uid != null;    
   }


 }
}

Does anybody knows what I'm doing wrong?

Update, added the code that I use to create the user document in the users collection:

signup () {
  if (this.alias && this.email && this.password) {
    this.slug = slugify(this.alias, {
      replacement: '-',
      remove: /[$*_+~.()'"!\-:@]/g,
      lower: true
    })
    let ref = db.collection('users').doc(this.slug)
    ref.get().then(doc => {
      if (doc.exists) {
        this.feedback = 'This alias already exists'
      } else {
        firebase.auth().createUserWithEmailAndPassword(this.email, this.password)
          .then(cred => {
            ref.set({
              alias: this.alias,
              household: this.household,
              user_id: cred.user.uid
            })
          }).then(() => {
            this.$router.push({ name: 'Dashboard' })
          })
          .catch(err => {
            console.log(err)
            this.feedback = err.message
          })
        this.feedback = 'This alias is free to use'
      }
    })
  }
}
2
@Herohtar the second code blog is triggered when the page is created. So this.user = doc.data().user_id. - user6740794
@Herohtar I added the code to create the user document in the question description. - user6740794

2 Answers

2
votes

Part of the problem is that you're trying to search for a document where the user_id field is equal to the user's ID, but your security rules are saying, "Only let users read a document if the ID of the document is the same as the user's ID", which is completely unrelated, and I don't know if that's actually true in your case.

One option is to change your rules where you say, "Hey, you can read / change a document if the value of the user_id field is equal to your user ID, which would be something like this...

match /users/{userId} {
  allow read, update, delete: if request.auth.uid == resource.data.user_id;
  // ...
}

...or to change your query so that you're querying for the specific document whose ID is the UID of the current user.

 let ref = db.collection('users').document(currentUser.uid)

 ref.get() {... }

But, I guess, don't do both at the same time. :)

1
votes

The error is coming from the first line where you try to get the current user:

ref.where('user_id', '==', firebase.auth().currentUser.uid).get()

This line is selecting documents where the user_id field matches the current user's ID, but your Firestore rule is checking to see if the user's ID matches the ID of the document. Since you're generating the document ID with slugify, it doesn't match, which is causing the permissions error.

Todd's suggestion for the rule change will work, but it would probably be best to just use the user ID as the document ID and store the user's slug as a field. That way you don't have to worry about collisions (user IDs will automatically be unique) and don't have to use a select statement -- you will know there is only one document per user and can just access it directly:

ref.doc(firebase.auth().currentUser.uid).get()

As a side note, you don't actually seem to have any reason to retrieve the user document at that point (at least in your example code). If you're really only using that to get the user_id field, you can just skip it since you already have the user ID from the auth session. In that case you should just do this:

this.user = firebase.auth().currentUser.uid
db.collection('submits').where('user', '==', this.user).get()
  .then(snapshot => {
    // console.log(snapshot)
    snapshot.forEach(doc => {
      let submit = doc.data()
      submit.id = doc.id
      submit.timestamp = moment(doc.data().timestamp).format('lll')
      this.previousSubmits.push(submit)
    })
  })