Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cloud Firestore Security Rules - only allow write to specific key in document

I'm currently writing some rules for my app with a Firestore database. Currently everyone can read data and authenticated users can write.

  match /quizzes/{quizId} {
    allow read; 
    allow write: if request.auth != null;
  }

That works fine, but I also want unauthenticated users to write only to a specific key in a document.

Example content of a document:

{
  title: 'title',
  plays: 12,
  playedBy: [//Filled with user id's],
  ...
}

Is there any way that limits unauthenticated users to only have write access to the playedBy array and not the other keys of that document?

like image 944
PeeJee Avatar asked Oct 21 '25 14:10

PeeJee


2 Answers

Sure thing. But it may become a bit involved if you have a lot of fields.

Let's start with the simplest example. Something like this allows an unauthenticated user to write the playedBy as long as that is the only field in the document:

if request.auth != null || request.resource.data.keys().hasOnly(['playedBy'])

This works if the unauthenticated user is creating a new document, or updating an existing one. But it will stop as soon as the document contains more fields, since request.resource.data contains all fields the document will have after the write succeeds.

So the better alternative is to check that only the playedBy is modified, and that all other fields have the same value as before. The tricky bit there is handling the non-existence of fields, which I typically handle with a few helper functions:

function isUnmodified(key) {
  return request.resource.data[key] == resource.data[key]
}
function isNotExisting(key) {
  return !(key in request.resource.data) && (!exists(resource) || !(key in resource.data));
}

And then:

if request.auth != null &&
  request.resource.data.keys().hasOnly(['title', 'plays', 'playedBy']) &&
  isUnmodified('title') && 
  isUnmodified('plays')

The exact rule might be a bit off, but I hope this is enough to allow you to complete it yourself.

like image 197
Frank van Puffelen Avatar answered Oct 24 '25 08:10

Frank van Puffelen


After the earlier answer (late 2019, I believe), Firebase has brought in Map.diff.

Something like:

match /quizzes/{quizId} {
    allow read; 
    allow write: if request.auth != null ||
      request.resource.data.diff(resource.data).affectedKeys() == ["playedBy"].toSet()
  }

Tested code where I use it can be seen here.

like image 39
akauppi Avatar answered Oct 24 '25 10:10

akauppi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!