在使用 firebase/auth 和 react-native 验证 his/her 电子邮件后,如何在不创建整个登录页面的情况下立即登录用户?

How can I log in a user right after his/her email has been verified using firebase/auth and react-native without creating a whole landing page?

注意:我见过this question,但是为了验证用户而创建整个着陆页似乎有点过分。

我使用 firebase/auth 通过电子邮件和密码向我的本机反应应用程序添加了登录功能。到目前为止效果很好,我没有遇到任何问题。

然后我继续向新用户发送一封验证电子邮件,并且在验证电子邮件后只允许 him/her 使用该应用程序。同样,这里没有问题。

下一步是在验证电子邮件后立即登录用户。这就是我卡住的地方,因为 onAuthStateChanged 事件处理程序在用户按下电子邮件中的验证 link 后不会更新。

有没有办法实时监听emailVerified状态?我尝试使用 setInterval() 进行轮询,但这不是很好,因为验证和登录之间存在明显的延迟。我读到了一个可以传递给 sendEmailVerification 的 continueLink,但我不知道如何在 react-native 中实现它。

我使用的是 Expo,因此是 Firebase SDK,而不是 Firebase React Native 包。

这是我用于注册的代码:

export const signUp = async (username: string, email: string, password: string) => {
    try {
        const auth = getAuth();
        if (email && password && username) {
            // sign up 
            const userCredential = await createUserWithEmailAndPassword(auth, email, password);
            // save username in firestore
            await setUserName(userCredential, username);
            // send Email Verification
            await sendEmailVerification(userCredential.user);
            return true;
        }
    } catch (error) {
        onError(error);
    }
};

这是我的 onAuthStateChanged 处理程序:

auth.onAuthStateChanged(authenticatedUser => {
            try {
                if (authenticatedUser?.emailVerified) {
                    setUser(authenticatedUser)
                } else {
                    setUser(null)
                }
            } catch (error) {
                console.log(error);
            }
        });

所以最后我确实遵循了this question,但是为了适应我的需要我做了一些改动。我会 post 我的步骤给任何做同样事情的人。

  1. 使用 firebase init 创建一个简单的静态网站并将其托管在 firebase 或其他地方(检查您的 firebase 控制台中的托管选项卡以开始使用)
  2. 按照 this guide 在网站上创建适当的处理程序
  3. 将以下内容添加到您的 verificationHandler 以更新用户(不要忘记导入 firestore)(我通过 continueURL 发送用户 ID,但可能有更好的方法)
// You can also use realtime database if you want
firebase.firestore().collection("users").doc(userId).set({
                            emailVerified: true
                        }, {merge: true}).then(() => {
                            message.textContent = "Your email has been verified.";
                        }).catch((error) => {
                            message.textContent = "The verification was invalid or is expired. Please try to send another verification email from within the app.";
                        });
  1. 在您的 firebase 控制台中进入身份验证 -> 模板并将操作 url 更改为您托管网站的 url
  2. 将 firestore 文档的侦听器添加到您的 react-native 应用程序
const onUserDataChanged = (uid, callback) => {
   onSnapshot(doc(firestore, "users", uid), doc => callback(doc.data()));
}
  1. 使用回调中的数据更新应用中的登录状态
// As an example
auth.onAuthStateChanged(authenticatedUser => {
             if (authenticatedUser && !authenticatedUser.emailVerified) {
                    unsubscribeFirestoreListener?.();
                    unsubscribeFirestoreListener = onUserDataChanged(authenticatedUser.uid, (data: any) => {
                        if (data?.emailVerified) {
                            setUser(authenticatedUser);
                            unsubscribeFirestoreListener?.();
                        }
                    });
             }
}

将下面的代码用于您的身份验证上下文。对于用户 ID,您应该使用 'user.uid'

import React, { useState, createContext } from "react";
import * as firebase from "firebase";

import { loginRequest } from "./authentication.service";

export const AuthenticationContext = createContext();

export const AuthenticationContextProvider = ({ children }) => {
const [isLoading, setIsLoading] = useState(false);
      const [user, setUser] = useState(null);
      const [error, setError] = useState(null);
      
       firebase.auth().onAuthStateChanged((usr) => {
        if (usr) {
          setUser(usr);
          setIsLoading(false);
        } else {
          setIsLoading(false);
        }
      });
      
      const onLogin = (email, password) => {
        setIsLoading(true);
        firebase.auth().signInWithEmailAndPassword(email, password)
          .then((u) => {
            setUser(u);
            setIsLoading(false);
          })
          .catch((e) => {
            setIsLoading(false);
            setError(e.toString());
          });
      };

      const onRegister = (email, password, repeatedPassword) => {
        setIsLoading(true);
        if (password !== repeatedPassword) {
          setError("Error: Passwords do not match");
          return;
        }
        firebase
          .auth()
          .createUserWithEmailAndPassword(email, password)
          .then((u) => {
            setUser(u);
            setIsLoading(false);
          })
          .catch((e) => {
            setIsLoading(false);
            setError(e.toString());
          });
      };

      const onLogout = () => {
        setUser(null);
        firebase.auth().signOut();
      };

      return (
        <AuthenticationContext.Provider
          value={{
            isAuthenticated: !!user,
            user,
            isLoading,
            error,
            onLogin,
            onRegister,
            onLogout,
          }}
        >
          {children}
        </AuthenticationContext.Provider>
      );
    };