0
votes

Hi I'm trying to read a users document stored on Firestore using Firebase Functions. Each user has a unique document with extra data that cannot be stored on Firebase Auth. The document name is the user UID. But I can't access the doc when I'm trying to read it on my callable function.

Code to create doc when user is created:

exports.createdacc = functions.auth.user().onCreate(user => {

console.log('User created', user.phoneNumber);
return admin.firestore().collection('users').doc(user.uid).set({
    number: user.phoneNumber,
    verified: false,
});

});

Callable function to read that doc so I can make some decisions

exports.checkVerification = functions.https.onCall((data, context) => {
if (!context.auth){
    throw new functions.https.HttpsError('unauthenticated');
}
console.log('user is ', context.auth.uid);
const user = admin.firestore().collection('users').doc(context.auth.uid);
user.get().then(doc => {    
    //temp code -- Not working 
    console.log('data read');
    if (doc.get().verified){
        console.log('verified');
    } else {
        console.log('not verified');
    }
    return "success";
}).catch(error => {
    throw new functions.https.HttpsError('internal');
});
});

Why cant I read the doc? Nothing inside there executes.

3
Did you check whether doc is created or not in firebase consoledevelopment-ninja
@development-ninja yes the doc exists. I can even access it from android using the library.Dinindu Perera
Ok and did you get correct context.auth.uid inside callable function?development-ninja
@development-ninja yep the uids match.Dinindu Perera

3 Answers

1
votes

Try to use data() at callback of user.get()

user.get().then(doc => {    
    //you get user doc value by using data()
    const userData = doc.data();
    // then you can use all properties from userData
    const verified = userData.verified;
});
0
votes

You don't return the promise returned by user.get().then(...);: your Cloud Function may be cleaned up before the asynchronous work is complete and the response sent back to the front-end.

Note that doing doc.get().verified is incorrect: as you will see in the doc, you need to pass the field path of a specific field to this method. So either you do doc.get("verified") or you can do doc.data().verified;.

Therefore the following should work:

exports.checkVerification = functions.https.onCall((data, context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('unauthenticated');
    }
    console.log('user is ', context.auth.uid);
    const user = admin.firestore().collection('users').doc(context.auth.uid);

    return user.get().then(doc => {

        console.log('data read');
        if (doc.get("verified") {
            console.log('verified');
        } else {
            console.log('not verified');
        }
        return "success";
    }).catch(error => {
        throw new functions.https.HttpsError('internal');
    });
});

In addition, note that you may throw an error if the user document does not exist and return a specific error to the front-end, i.e. not the generic internal one (maybe not-found, see the list of possible codes).

0
votes

I have seen, on occasion, that information coming in to the function via context and data are actually JSON, and not strictly a standard Javascript object. In a similar issue of matching (in my case, a customClaim on the context.auth.token), I had to do something like:

JSON.parse(JSON.stringify(context.auth.token.customCLaim))

They behave like an object (i.e. I can call/assign context.auth.token.customClaim), but results from a console.log are different.

console.log(context.auth.token.customCLaim);
//prints {"userID": "1234567890"}

console.log(JSON.parse(JSON.stringify(context.auth.token.customClaim)));
//prints {userID: "1234567890"}

Subtle, but it tripped me up in a few authentication cases.