如何在使用 firebase/auth 和 react-native 验证他/她的电子邮件后立即登录用户而不创建整个登录页面?

Posted

技术标签:

【中文标题】如何在使用 firebase/auth 和 react-native 验证他/她的电子邮件后立即登录用户而不创建整个登录页面?【英文标题】: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? 【发布时间】:2021-12-23 06:11:09 【问题描述】:

注意:我见过this question,但是创建一个完整的登录页面只是为了验证用户似乎有点过分。

我使用 firebase/auth 以及电子邮件和密码向我的 react-native 应用程序添加了登录功能。到目前为止效果很好,我没有问题。

然后,我继续向新用户发送验证电子邮件,并且仅在电子邮件通过验证后才允许他/她使用该应用程序。同样,这里没有问题。

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

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

我使用的是Expo,因此使用的是 Firebase SDK,而不是 Firebase 反应原生包。

这是我用于注册的代码:

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

【问题讨论】:

【参考方案1】:

所以最后我确实关注了this question,但我对其进行了一些更改以满足我的需要。我会将我的步骤发布给任何这样做的人。

    使用 firebase init 创建一个简单的静态网站,并将其托管在 Firebase 或其他地方(检查您的 Firebase 控制台中的托管选项卡以开始使用) 关注this guide 在网站上创建相应的处理程序 将以下内容添加到您的 verificationHandler 以更新用户(不要忘记导入 firestore)(我通过 continueURL 发送 userId,但可能有更好的方法)
// 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.";
                        );
    进入身份验证 -> Firebase 控制台中的模板并将操作 url 更改为托管网站的 url 为您的 react-native 应用添加一个到 firestore 文档的侦听器
const onUserDataChanged = (uid, callback) => 
   onSnapshot(doc(firestore, "users", uid), doc => callback(doc.data()));

    使用回调中的数据更新应用中的登录状态
// As an example
auth.onAuthStateChanged(authenticatedUser => 
             if (authenticatedUser && !authenticatedUser.emailVerified) 
                    unsubscribeFirestoreListener?.();
                    unsubscribeFirestoreListener = onUserDataChanged(authenticatedUser.uid, (data: any) => 
                        if (data?.emailVerified) 
                            setUser(authenticatedUser);
                            unsubscribeFirestoreListener?.();
                        
                    );
             

【讨论】:

【参考方案2】:

将以下代码用于您的身份验证上下文。对于用户 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>
      );
    ;

【讨论】:

以上是关于如何在使用 firebase/auth 和 react-native 验证他/她的电子邮件后立即登录用户而不创建整个登录页面?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Firebase/Auth 上避免 UIWebView

如何观察 firebase.auth().currentUser.displayName 和其他属性的变化?

如何将来自不同组件的 firebase auth 和 Cloud Firestore 用作单个 Firebase 应用程序

如何从应用程序远程强制注销firebase auth用户

如何捕获 Firebase Auth 特定异常

如何在使用 firebase/auth 和 react-native 验证他/她的电子邮件后立即登录用户而不创建整个登录页面?