Firestore Security Rules

Firestore Security Rules is one of the most important aspect of Firestore usage:

  • Security Rules only apply to mobile and web client libraries, doesn't apply to server client libraries. Technically, user could manipulate mobile/web client, but not server code.
  • If you don't design the security rules properly, user can create/update/delete any data/documents. It is like configuring the security/permission of REST APIs.

Be Restrictive

Make sure all documents don't allow read/write by default.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}

Even if you want to do testing, only assign permission per collection when necessary.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }

    match /tests/{testId} {
      allow read: if true
      // must sigin
      allow write: if request.auth != null;
    }
  }
}

Check is Signin, is User, is Owner, is Admin

Check is Signin/Login.

match /tests/{testId} {  function isLogin() {    return request.auth != null  }  allow read: if true  // must sigin  allow write: if isLogin()}

NOTE: request.auth is populated if client use Firebase Authentication.

Check is valid User.

function isUser() {  return isLogin() && exists(/databases/$(database)/documents/users/$(request.auth.uid))}

NOTE: Assume we have a collection users to store all valid user.

Check if valid User is active.

function isUserActive() {  return isLogin() && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.active == true}

NOTE: Assume the field/property active is used in User document to indicate active or not.

Check if User is Admin

  • We can hardcode uid as Admin
  • or use field/property roles (an array) in User document (or you can use admin == true)
function isAdmin() {  return isLogin() &&    (request.auth.uid == 'QYHqCKCGSbY3qqMSliqNhR3j9QC2' ||     'admin' in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles)}

NOTE: Refer to Firestore Check Is Admin Security Rules for more alternatives.

Check is Owner

For users collection, where {userId} is the Document ID and could be used to check against request.auth.uid.

match /users/{userId} {
  function isOwner() {
    return userId == request.auth.uid;
  }
}

For other collection, we would need to check against the User ID property/field of the document.

match /comments/{commentId} {
  function isOwner() {
    return request.resource.data.userId == request.auth.uid;
  }
}

NOTE: The above check can prevent user from imitating another user, check checking resource.data.userId only doesn't.

Test Security Rules

Simulator

You can use the Simulator at Firebase Console -> Database -> Rules -> Simulator.

NOTE: Service call error. Function: [exists], Argument ... may happend if you enable Authenticated without Firebase UID while using rules like exists(/databases/$(database)/documents/users/$(request.auth.uid))

NOTE: I bump into error Function not found error: Name: [get] when using get(/databases/$(database)/documents/users/$(request.auth.uid)) while using the Simulator, and it works fine in production. This is probably a temporary bug.

Cloud Firestore Emulator

You can use Cloud Firestore Emulator.

Use Android Unit Test

Firestore Test Production Security Rules With Android Unit Test

References:

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.