Dynamic Google Cloud Firestore rules on document ID

Viewed 87

Thanks for taking the time to read!

What I'm trying to do is dynamically compare the requested document ID against another string saved in the custom claims of a user, using Google Firestore.

I've added the code below (removed the unimportant rules for this question) which from reading the docs seems to be correct, but when I try and use these rules it always returns to false. In fact, when I compare orgId to a string of the document like orgId == 'T90101' that also returns false.

What am I missing?

rules_version = '2';
service cloud.firestore {
    match /databases/{database}/documents {
      // Organization ID saved on the custom claims for this user
      function getMyOrgId() {
        return request.auth.token.organizationId
      }
    
      // BUG: This equates to false but should be true
      function verifyOrganizationClaim(orgId) {
        return getMyOrgId() == orgId;
      }
    
      match /organizations/{orgId} {
        allow create: if false;
        allow delete: if false;
        allow update: if verifyOrganizationClaim(orgId);
        allow read: if verifyOrganizationClaim(orgId);
      } 
   }
}

In summary - how do I compare a document ID against a saved custom claim ID, to restrict access to documents?

Any help would be appreciated!


Here's the code I'm using to access this data on the client side using React + TypeScript. The error I get is Uncaught (in promise) FirebaseError: Missing or insufficient permissions.

import React from "react";
import firebase from "firebase";
import { useDocumentDataOnce } from "react-firebase-hooks/firestore";

export const firestore = firebase.firestore();

const MY_ORG_ID = "T01JLKU9W7L"; // This is the same value saved in the custom claims for this user
const EXTERNAL_ORG_ID = "T01JLKU9W7L";

interface Organization {
  id: string;
  displayName: string;
}

const Example: React.FC = () => {
  const [myOrg] = useDocumentDataOnce<Organization>(
    firestore.doc(`organizations/${MY_ORG_ID}`)
  );

  const [externalOrg] = useDocumentDataOnce<Organization>(
    firestore.doc(`organizations/${EXTERNAL_ORG_ID}`)
  );

  return (
    <>
      {/** Should succeed */}
      <div>
        <h1>My Organization</h1>
        <p>Organization Name: {myOrg?.displayName}</p>
        <p>Organization Id: {myOrg?.id}</p>
      </div>
      {/** Should fail */}
      <div>
        <h1>Other Organization</h1>
        <p>Organization Name: {externalOrg?.displayName}</p>
        <p>Organization Id: {externalOrg?.id}</p>
      </div>
    </>
  );
};

export default Example;

1 Answers

I've tried your Security Rules in one of my test environment and I can confirm that the if verifyOrganizationClaim(orgId); statement does work (together with the two functions).

So I can see three possible reasons why it does not work on your side:

  1. Your user is not authenticated
  2. You incorrectly assigned the Custom Claim to the user. You can check the custom claims with the following CLI command: $ firebase auth:export users.csv
  3. You try to create a document, instead of updating or reading it. As a matter of fact your rules only set access rights for updating or reading. (I'm not verse in React and I cannot deduce from the code in your question which operation you execute...)
Related