menaka
menaka

Reputation: 1068

Firebase onCreate add custom claims to the auth.user

I am trying to add a custom claims, isRegistered to firebase. My firestore has another user collection to keep register info records. Now I am trying to keep a isRegistered custom claim but I can not seem to get it work.

exports.addRegisteredRole = functions.database.ref('/user')
    .onCreate((snap, context) => {
      return // **I added this later, but the issue still remains.**
        admin.auth()
            .setCustomUserClaims(context.auth.uid, {isRegistered: true})
            .then(() => {
                console.log('done', snap)
                return {
                    message: 'done',
                    data: snap
                }
            })
            .catch(err => {
                console.log('something went wrong', err);
                return err
            })
    });

I am checking this claim by,

currentUser.getIdTokenResult()
        .then(res => {
            console.log(res.claims.isRegistered)
        })

(auth user object). Even if I re-logged it remains undefined. Am I doing something wrong, I am very new to firebase.

Upvotes: 4

Views: 4620

Answers (4)

fabyeah
fabyeah

Reputation: 55

Most likely the problem is that you didn't propagate the new custom claims to the client. See here: https://firebase.google.com/docs/auth/admin/custom-claims#propagate_custom_claims_to_the_client

This github issue points that out as well: https://github.com/firebase/firebase-tools-ui/issues/424

If you log out and log back in with the user, it should work. Simply refreshing the page for a logged in user doesn't refresh the user's ID token.

As setting custom claims in the onCreate trigger is a common use case, it would be nice to have a note on this in the docs (https://firebase.google.com/docs/auth/admin/custom-claims) specifically for firestore, as the given example is for the realtime db.

Upvotes: 1

MAW
MAW

Reputation: 973

The issue is with your onCreate trigger. You assumed you're getting the uid in the context.auth object, which is not correct.

The onCreate trigger will be triggered automatically on the addition of a new document in your "user" collection. In this case, the context.aut.uid is undefined. You should trace this in your function logs.

You can achieve what you are trying to do in a couple of ways

  1. If the user record document has the the user Id as the name of the document, you can do the following
exports.addRegisteredRole =
  functions.firestore
    .document('test/{docId}')
    .onCreate((snap, context) => {
      admin.auth()
        .setCustomUserClaims(snap.id, { isRegistered: true })
        .then(() => {
          console.log('done', snap)
          return {
            message: 'done',
            data: snap
          }
        })
        .catch(err => {
          console.log('something went wrong', err)
          return err
        })
    })
  1. If you're naming the user record document with something else, or letting firebase decide the name for you, then you must have the uid as an attribute in the document data. Then you use docSnapshot.data().uid instead of docSnapshot.id as follows
exports.addRegisteredRole =
  functions.firestore
    .document('test/{docId}')
    .onCreate((snap, context) => {
      admin.auth()
        .setCustomUserClaims(snap.data().uid, { isRegistered: true })
        .then(() => {
          console.log('done', snap)
          return {
            message: 'done',
            data: snap
          }
        })
        .catch(err => {
          console.log('something went wrong', err)
          return err
        })
    })

Good luck

Upvotes: 1

Sloosh
Sloosh

Reputation: 1691

I think you should use functions.firestore.document with wildcard to trigger your "user" documents:

exports.addRegisteredRole = functions.firestore.document('/user/{userId}')
.onCreate((snap, context) => { ... });

Upvotes: 0

Frank van Puffelen
Frank van Puffelen

Reputation: 599041

I suspect that your Cloud Function gets terminated before the call to Auth completes. The reason for that is that you're not returning anything from the top-level of your code, which means that Cloud Functions assumes that your code is done once the final } runs. But since the call to `` is asynchronous, that call hasn't completed yet and for example your console.log('done', snap) won't have run yet.

exports.addRegisteredRole = functions.database.ref('/user')
.onCreate((snap, context) => {
    return admin.auth()
        .setCustomUserClaims(context.auth.uid, {isRegistered: true})
        .then(() => {
            console.log('done', snap)
            return {
                message: 'done',
                data: snap
            }
        })
        .catch(err => {
            console.log('something went wrong', err);
            return err
        })
});

Upvotes: 0

Related Questions