skip to Main Content

I want to make Firestore’s rule using Firebase custom token like this.


match /post/{postId}{
      allow create: if request.auth.token[role] == `admin`;
      allow read: if request.auth.token[role] == `admin`;   
 }

When a user sign up an app, I want to give her its role using cloud functions.
This is here.

const functions = require('firebase-functions');


export const updateAcl = functions
  .firestore.document("/members/{memberId}")
  .onCreate(async(user, context) => {
      await getAuth().setCustomUserClaims(user.uid, {role:'admin'});
  });

The flow is here.

  1. A user sign up the app.
  2. The user will be given an role in async backend.
  3. Using custom claim the content will appear.

But this has a problem. Because of the timing.
When the user got the role using backend’s async cloud functions, the content already appeared.
First time when it shows, her doesn’t have her role yet.
I mean before the content appear, her should have her role.

Cloud functions onCreate doesn’t have sync now.
How can we solve it?

2

Answers


  1. Custom claims won’t refresh in rules immedately untill they Propagate custom claims to the client, You can force refresh ID token by calling currentUser.getIdToken(true) to propagate custom claims on client after you call setCustomUserClaims via admin sdk.

    All I can think to tell client after custom claims changed is write data to firestore or rtdb:

    export const initUser = functions
      .auth.user()
      .onCreate(async(user, context) => {
          await getAuth().setCustomUserClaims(user.uid, 'admin');
    // Write data to database to tell user claims are changed then let client listen to this document.
      });
    

    Alternative, also is my prefer one, is simply just use firestore to implement Role-Based Access Control, You can use admin uid as document id, then write this document to a collection call admins, And rules look like this:

    function isAdmin(){
      return exists(/databases/$(database)/documents/admins/$(request.auth.uid));
    }
    
    match /post/{postId}{
      allow create, read: if isAdmin();   
    }
    
    Login or Signup to reply.
  2. You can use a beforeCreate blocking function. You’ll need to upgrade your project to Firebase Authentication with Identity Platform (it’s free up to 50K MAUs IIRC)

    You can then write your function like this:

    export const initUser = functions
      .auth.user()
      .beforeCreate((user, context) => {
          return {
            customClaims: { admin: true }
          }
      });
    

    The Firebase docs have an example

    Don’t forget to register your blocking function in the Authentication console. (I’m not sure if the "non blocking" functionality of them will still work without registration, but you definitely need to register the function if you want to be able to block user sign in/up)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search