6

I'm struggling with firebase custom claims.

I have tested a lot of approaches nothing works. Obviously, I miss something important in the concept itself.

So I'm back to the root. This script from the google example should apply customs rule on a newly created user

exports.processSignUp = functions.auth.user().onCreate(event => {
  const user = event.data; // The Firebase user.
  const customClaims = {
      param: true,
      accessLevel: 9
    };
  // Set custom user claims on this newly created user.
  return admin.auth().setCustomUserClaims(user.uid, customClaims)   

});

Then on a client, I check the result with

firebase.auth().currentUser.getIdTokenResult()
                .then((idTokenResult) => {
                    // Confirm the user is an Admin.
                    console.log(idTokenResult.claims)
                    if (!!idTokenResult.claims.param) {
                    // Show admin UI.
                    console.log("param")
                    } else {
                    // Show regular user UI.
                    console.log("no param")
                    }
                })
                .catch((error) => {
                    console.log(error);
                });

Everything just a raw copy-paste still doesn't work. I've tested both from the local machine(there could be troubles with cors?) and deployed

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Dmitriy Karpovich
  • 309
  • 2
  • 4
  • 11

2 Answers2

7

This is a race situation. If the Function end first then, you will get the updated data.

The getIdTokenResult method does force refresh but if the custom claim is not ready then, it is pointless.

You need to set another data control structure to trigger the force refresh on the client. By example a real-time listener to the rtd;

root.child(`permissions/${uid}`).on..

And the logic inside the listener would be: if the value for that node exists and is a number greater than some threshold, then trigger the user auth refresh

During that time the ui can reflect a loading state if there is no datasnapshot or the not admin view if the datasnapshot exists but is a lower permission level.

In Functions you have to set the node after the claim is set:

..setCustomUserClaims(..).then(
    ref.setValue(9)
);

I have a more detailed example on pastebin

cutiko
  • 9,887
  • 3
  • 45
  • 59
2

The claims on the client are populated when the client gets an ID token from the server. The ID token is valid for an hour, after which the SDK automatically refreshes it.

By the time the Cloud Functions auth.user().onCreate gets called, the client has already gotten the ID token for the new user. This means that it can take up to an hour before the client sees the updated claims.

If you want the client to get the custom claims before that, you can force it to refresh the token. But in this video our security experts recommend (that you consider) using a different storage mechanism for claims that you want to be applied straight away.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • You know, I was back after the weekend, have pressed the logout, login again, and there is still not a custom claim in the console - console.log(idTokenResult.claims) – Dmitriy Karpovich Feb 11 '19 at 07:43
  • Hmm... I don't know why that would be, given that the above flow is how the custom claims make it onto the client. You might want to double check if the custom claim actually made it into the profile to begin with with the Admin SDK. But aside from that also check the video I linked, since this might be a use-case where custom claims are not your preferred solution. – Frank van Puffelen Feb 11 '19 at 15:04