首页 > 解决方案 > 如何仅在functions.auth.user().onCreate完成后才能完成登录

问题描述

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

只有当所有东西都来自

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 等)

标签: firebasegoogle-cloud-firestorefirebase-authenticationgoogle-cloud-functions

解决方案


AFAIK 无法直接耦合应用程序中隐含的两个进程:

  • 一方面,您在前端实现了 Google 登录流程(即使后端调用了 Auth 服务),并且;
  • 另一方面,您拥有在后端执行的云功能。

您遇到的问题来自这样一个事实,即一旦Google 登录流程成功,您的用户就会登录到您的应用程序并尝试读取由 Cloud Function 创建的文档。

在某些情况下(例如由于 Cloud Function 冷启动),当用户登录时尚未创建此文档,从而导致错误。

一种可能的解决方案是在前端设置一个 Firestore 侦听器以等待创建此文档,如下所示。请注意,以下代码仅考虑了由该addFirst()函数创建的 Firestore 文档,因为您没有提供有关要通过创建的第二个文档的任何详细信息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;
  // ...
});

如上所述,在上面的代码中,我们只考虑了该函数创建的第一个Firestore 文档。addFirst()但是您可能需要等待两个文档创建完成,然后才能从前端读取它们。

因此,您可以按如下方式修改您的 CF:

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块中是您应该为可调用云函数处理错误的方式。在这里,您正在编写一个后台触发的云函数,您可以使用return null;


推荐阅读