2

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?

Takeshi
  • 59
  • 5
  • To ensure that the custom claim token is synced properly with Firebase Auth, you can modify your code to include a call to the `setCustomUserClaims` function before the user signs in. This can be done by modifying the sign-up function to include a call to the `setCustomUserClaims` function. By doing so, the custom claim token will be synced with Firebase Auth before the user logs in, and the Firestore rules should work as expected. – Chanpols Mar 24 '23 at 20:16
  • What do you mean? To give a user `setCustomUserClaims`, I have to know her uid. So before her sign up, I can't give her `setCustomUserClaims` using her uid. I think before using custom claim the content will appear, the app have to wait until her is given `setCustomUserClaims`. Because of the sync. Do you have something to resolve? – Takeshi Mar 25 '23 at 08:45

2 Answers2

2

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();   
}
flutroid
  • 1,166
  • 8
  • 24
  • Also see this [Firebase custom claims are not update in rules](https://stackoverflow.com/questions/58280361/firebase-custom-claims-are-not-update-in-rules) – flutroid Mar 25 '23 at 12:38
  • How is backend's cloud functions synced with frontend using `currentUser.getIdToken(true)`? Signup triggers backend's cloud functions. But frontend does't know the result. So I don't know when I should use `currentUser.getIdToken(true)`. So it means how to sync custom claim token. – Takeshi Mar 25 '23 at 13:54
  • At first I thought user should listen to the firestore document about role. But if all users do, not good. Because role is so important in point of the security. So I think frontend does't have the role's information. And I had the option to use check Firestore document in the rule. But I didn't use it. Because if the money cost. Every user access the data, the check functions will trigger. So I thought I should use custom claim token. But I don't know when users should use `currentUser.getIdToken(true)`. I want to confirm after sign up and before a content shows. – Takeshi Mar 26 '23 at 10:45
  • 1
    According to this [ow do I listen to changes in Firebase custom claims?](https://stackoverflow.com/questions/75047152/how-do-i-listen-to-changes-in-firebase-custom-claims) Says it's not possible to listen custom claims changes on client, So if you really want to save your bills, maybe consider just hardcode admins uid in rules: `request.auth.uid in ["admin_id1","admin_id2"]` , But I still perfer use firestore only, it's more simpler and easy to implement. – flutroid Mar 26 '23 at 13:08
  • 1
    Based on your advices, I have to use firestore listeners. Because if an owner change a user's acl, her should be quickly reflected. I used `currentUser.getIdToken(true)` in Firestore listener. I did use a `functions.https.onCall` not onCreate in Firestore. I did archived the problem. Big thanks for your support. – Takeshi Mar 26 '23 at 13:46
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)

grimsteel
  • 796
  • 1
  • 17
  • 1
    This should work well, Because you create account with custom claims. – flutroid Mar 25 '23 at 23:29
  • I don't know how to use the answer at this situation. I want to give users different roles like admin, reader and viewer. I think the way you presented to me is always to give all users admin. And I edited my post. When a firestore document is written, the cloud functions will trigger. How can a client listen to the custom claim token's change? – Takeshi Mar 26 '23 at 10:33