温馨提示:本文翻译自stackoverflow.com,查看原文请点击:firebase - How to complete login only after functions.auth.user().onCreate is finished
firebase google-cloud-firestore google-cloud-functions firebase-authentication

firebase - 仅在functions.auth.user()。onCreate完成后如何完成登录

发布于 2020-03-27 16:04:13

我正在使用firebase函数,并且具有一个在用户创建时添加新集合的函数。问题是有时用户在功能完成之前已登录,因此用户已登录但尚未创建新集合(然后我收到错误消息“缺少或权限不足。因为规则找不到该集合”)。我该如何处理?

仅当来自以下位置的所有内容时,才能完成登录用户(例如使用Google提供程序)吗

export const createCollection = functions.auth.user().onCreate(async user => {
  try {
    const addLanguages = await addFirst();
    const addSecondCollection = await addSecond();

    async function addFirst() {
      const userRef = admin.firestore().doc(`languages/${user.uid}`);
      await userRef.set(
        {
          language: null
        },
        { merge: true }
      );

      return 'done';
    }

    async function addSecond() {
      // ...
    }

    return await Promise.all([addLanguages, addSecondCollection]);
  } catch (error) {
    throw new functions.https.HttpsError('unknown', error);
  }
});

完成了吗?因此,谷歌提供者窗口是关闭的,只有在那之后用户才能登录?(并且不要使用setTimeouts等)

查看更多

查看更多

提问者
Przemo
被浏览
129
Renaud Tarnec 2020-01-31 21:13

AFAIK it is not possible to directly couple the two processes implied in your application:

  • On one hand you have the Google sign-in flow implemented in your front-end (even if there is a call to the Auth service in the back-end), and;
  • On the other hand you have the Cloud Function that is executed in the back-end.

The problem you encounter comes from the fact that as soon as the Google sign-in flow is successful, your user is signed in to your app and tries to read the document to be created by the Cloud Function.

In some cases (due for example to the Cloud Function cold start) this document is not yet created when the user is signed in, resulting in an error.

One possible solution would be to set a Firestore listener in your front-end to wait for this document to be created, as follows. Note that the following code only takes into account the Firestore document created by the addFirst() function, since you don't give any details on the second document to be created through addSecond().

firebase.auth().signInWithPopup(provider)
.then(function(result) {
  var token = result.credential.accessToken;
  var user = result.user;  
  //Here we know the userId then we can set a listener to the doc languages/${user.uid}

  firebase.firestore().collection("languages").doc(user.uid)
    .onSnapshot(function(doc) {
       if(doc.exists) {
         console.log("Current data: ", doc.data());
         //Do whatever you want with the user doc
       } else  {
         console.log("Language document not yet created by the Cloud Function");
       }

    });
}).catch(function(error) {
  var errorCode = error.code;
  var errorMessage = error.message;
  var email = error.email;
  var credential = error.credential;
  // ...
});

As said above, in the above code we only take into account the first Firestore document created by the addFirst() function. But you probably need to wait for the two docs to be created before reading them from the front-end.

So, you may modify you CF as follows:

export const createCollection = functions.auth.user().onCreate(async user => {
  try {
    await addFirst();
    await addSecond();
    return null;

    async function addFirst() {
      const userRef = admin.firestore().doc(`languages/${user.uid}`);
      await userRef.set(
        {
          language: null
        },
        { merge: true }
      );
    }

    async function addSecond() {
      // ...
    }

  } catch (error) {
    console.log(error);
    return null;
  }
});

请注意,您不需要使用Promise.all():以下两行已经执行了两次写入Firestore的文档。并且,由于使用async/await第二个文档,因此只有在第一个文档写入后才能编写。

    const addLanguages = await addFirst();
    const addSecondCollection = await addSecond();

因此,您只需要在第二个文档的路径上设置侦听器,就可以完成!


最后,请注意

throw new functions.https.HttpsError('unknown', error);

在您的代码catch块中,您应该处理可调用云函数错误的方式。在这里,您正在编写后台触发的 Cloud Function,您可以使用return null;