Firestore Security Rules: Allow User To Create Doc Only If New Doc ID is same as User ID

Update

I recently had to update my rule set because of some changes to the way firestore rules worked, and changes in how the "getAfter" function works. Specifically, I am now able to use request.resource for data comarisons. Anyways, it appears that I can accomplish my goals with simpler rules now so I thought I'd update this answer and share.

Goals

  • User can create a document, only if the new document ID matches their user ID.
  • User cannot declare themselves an "admin", block create / update / write requests if "admin" is a field (unless they are already an admin)
service cloud.firestore {
  match /databases/{database}/documents {
  
    // Allow users to create a document for themselves in the users collection
    match /users/{document=**} {
      allow create: if request.resource.id == request.auth.uid &&
        !("admin" in request.resource.data);
    }
    
    // Allow users to read, write, update documents that have the same ID as their user id
    match /users/{userId} {   
        // Allow users to read their own profile (doc id same as user id)
      allow read: if request.auth.uid == userId;
      
      // Allow users to write / update their own profile as long as no "admin"
      // field is trying to be added or created - unless they are already an admin
      allow write, update: if request.auth.uid == userId &&
        (
          !("admin" in request.resource.data) ||
          get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true // allow admin to update their own profile
        )

      // Allow users to read their own feeds
      match /feeds/{document=**} {
        allow read: if request.auth.uid == userId;
      } 
    }
  }
}

Old Answer

So I figured out how to do this in a workaround way. I also had some additional write / update conditions that prevent the user from changing their permission level. This was for some reason, preventing any "creates" from happening. So I had to mirror the same conditions in create, and the write / update rules. For some reason this was necessary.

This new rule structure accomplishes the following

First Section, for create rule

  • allows the only authenticated users to create documents only in the "users" collection (during the user setup process, a document is created automatically with the same ID as their user id).
  • does not allow creation of a document containing the "admin" field, which would suggest they are trying to gain admin access.
  • it seems that validating the id of the document during creation is not possible, hence additional write / update rules below

Second Section - read, update, write

  • allows users to read / write / update only documents that have the same ID as their user id (user trying to create a document with an ID other than their user id will fail, also prevents the user from spamming creation of tons of docs by manipulating the client-side JS request.)
  • does not allow users to write / update their profile to include the "admin" field

Rules

service cloud.firestore {

      match /databases/{database}/documents {
      
        // Allow users to create documents in the user's collection
        match /users/{document=**} {
          allow create: if request.auth.uid != null &&
            !("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
        }
        
        // Allow users to read, write, update documents that have the same ID as their user id
        match /users/{userId} {   
            // Allow users to read their own profile (doc id same as user id)
          allow read: if request.auth.uid == userId;
          
          // Allow users to write / update their own profile as long as no "admin" field is trying to be added or created
          allow write, update: if request.auth.uid == userId &&
            !("admin" in getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data);
        }
      }
    }

PS This was not intuitive at all, so if someone has a better workaround, please post it. Also, I'm really hoping that once firestore 1.0 is out, it will bring with it some huge improvements to rules and rule documentation.