本指南是如何使用 React Web 应用程序设置 Firebase 身份验证的补充。在该指南的介绍中,我注意到 Firebase 提供了除基本电子邮件/密码身份验证之外的其他身份验证方案。这些替代方案之一是无密码身份验证。
在构建应用程序时,无密码身份验证是一个有吸引力的选择。它简化了用户体验,因为您的用户无需记住他们的密码,因此也不必担心丢失密码。它还简化了开发体验,因为无需设计任何密码捕获或管理逻辑。
在本指南中,您将构建一个简单的登录/确认/个人资料/注销工作流程来实现 Firebase 的无密码身份验证。
Google 设置了各种 Firebase 身份验证限制。如果您使用免费的Spark计划,请注意,您每天最多只能发送 5 封登录链接电子邮件。虽然 Spark 计划可能足以满足测试目的,但您需要升级到即用即付Blaze计划才能超过此限制。
在本指南中,我将参考如何使用 React Web 应用程序设置 Firebase 身份验证指南作为先决条件指南,并将关联的项目作为先决项目。
要完成本指南,您需要:
在先决条件指南中,您创建了一个用于基本电子邮件/密码身份验证的新 Firebase 项目。现在,您将为同一项目启用无密码身份验证。登录您的Firebase 帐户并点击Go to Console 。
单击 Firebase 项目仪表板上列出的身份验证项目。本指南使用项目名称my-auth-test
。
单击左侧面板菜单中的身份验证。
单击主窗口中的登录方法选项卡。
根据您在先决条件指南中的工作,登录提供程序表应该已在提供程序列下显示电子邮件/密码,状态为Enabled 。单击铅笔图标打开电子邮件/密码提供商的配置面板。
单击开关以启用电子邮件链接(无密码登录) 。
单击“保存” 。
您的 Firebase 项目现已配置为支持无密码身份验证。
使用您所需的应用程序名称创建一个新的 React 项目。本指南使用passwordless-auth
。
npx create-react-app passwordless-auth
本指南需要安装 3 个 Node.js 包:
通过npm
安装上述三个软件包:
npm install firebase
npm install react-router-dom
npm install bootstrap
firebase.js
在必备项目中,您创建了一个firebase.js
文件,该文件使用 Firebase 项目配置数据来创建 Firebase 身份验证服务的实例。您的firebase.js
文件应具有以下结构以及您的Firebase 项目配置值:
import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; const firebaseConfig = { apiKey: "AIzaSyDiUlY68W9Li_0EIkmdGdzD7nvqCT9kHnY", authDomain: "my-auth-test-fbd48.firebaseapp.com", projectId: "my-auth-test-fbd48", storageBucket: "my-auth-test-fbd48.appspot.com", messagingSenderId: "1078604952662", appId: "1:1078604952662:web:5d0b908439cfb5684ab7f7" } const app = initializeApp(firebaseConfig); const auth = getAuth(app); export { auth }
将firebase.js
复制到新的 React 项目文件夹。
如果您需要查看 Firebase 项目配置,请单击左侧面板菜单中“项目概述”旁边的齿轮图标。应已选择“常规”选项卡。向下滚动到包含Web 应用程序面板的“您的应用程序”部分。 Web 应用程序面板中的npm选项应该已被选择。您的项目配置值将在显示的代码块中列出。
React 应用程序将由 5 个组件组成: App
、 Layout
、 Login
、 Confirm
和Profile
。
App
App
组件定义了整个应用程序结构,包括路由。 Layout
Layout
组件指定在所有路由中保持一致的应用程序标记。 Login
Confirm
Confirm
页面。 Profile
Profile
页面。Profile
页面上的注销按钮来注销他/她的帐户。最终的src
目录将包含以下文件:
src |__ index.js |__ firebase.js // Copied from prerequisite project in Step 3. |__ App.js |__ Layout.jsx |__ Login.jsx |__ Confirm.jsx |__ Profile.jsx
reportWebVitals.js
setupTests.js
logo.svg
index.css
App.css
App.test.js
将index.js
从必备项目复制到新项目文件夹。本指南使用相同的文件。如果您需要重建index.js
,请参阅先决条件指南的步骤 5b.2 或复制下面的代码块。
将Layout.jsx
从必备项目复制到新项目文件夹。本指南使用相同的文件。如果您需要重建Layout.jsx
,请参阅先决条件指南的步骤 5d 或复制下面的代码块。或者,您可以将Layout.jsx
的<p>
标记中的项目文本更新为React With Firebase Passwordless Authentication
或任何您喜欢的标题。
您的index.js
和Layout.jsx
文件应如下所示:
// index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import "bootstrap/dist/css/bootstrap.min.css"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );
// Layout.jsx import { Outlet } from "react-router-dom"; const Layout = () => { return( <div className = "container-fluid"> <div className = "row justify-content-center mt-3"> <div className = "col-md-4 text-center"> <p className = "lead">React With Firebase Passwordless Authentication</p> </div> <Outlet /> </div> </div> ) } export default Layout
App.js
App.js
文件与必备项目中的文件基本相同,仅更改了两行。为了便于构建文件,请将App.js
从必备项目复制到新项目文件夹。
从文件中删除以下import
行并将其替换,如下所示:
// Delete this line: import Signup from "./Signup";
// Replace it with: import Confirm from "./Confirm";
从文件中删除以下<Route>
并将其替换,如下所示:
// Delete this line: <Route path = "/signup" element = { <Signup></Signup> } ></Route>
// Replace it with: <Route path = "/confirm" element = { <Confirm></Confirm> } ></Route>
保存App.js
。
完整的文件现在应如下所示:
import Layout from "./Layout"; import Login from "./Login"; import Confirm from "./Confirm"; import Profile from "./Profile"; import { BrowserRouter, Routes, Route } from "react-router-dom"; const App = () => { return ( <BrowserRouter> <Routes> <Route path = "/" element = { <Layout></Layout> }> <Route index element = { <Login></Login> }></Route> <Route path = "/confirm" element = { <Confirm></Confirm> } ></Route> <Route path = "/profile" element = { <Profile></Profile> } ></Route> </Route> </Routes> </BrowserRouter> ) } export default App
请注意, Login
组件再次成为应用程序的主路由,就像先决项目一样。
Login.jsx
在无密码身份验证的情况下,您显然不需要包含密码字段,也不需要管理密码输入的状态。因此,登录表单只需要捕获用户的电子邮件地址。
在src
目录中创建一个新的Login.jsx
文件。
添加以下代码:
import { useState } from "react"; import { auth } from "./firebase"; import { sendSignInLinkToEmail } from "firebase/auth"; const Login = () => { const [email, setEmail] = useState(""); const [notice, setNotice] = useState(""); const actionCodeSettings = { url: "http://localhost:3000/confirm", handleCodeInApp: true } const callSendSignInLinkToEmail = (e) => { e.preventDefault(); sendSignInLinkToEmail(auth, email, actionCodeSettings) .then(() => { setNotice("An email was sent to your email address. Click the link in the email to login."); }) .catch((error) => { setNotice("An error occurred when sending a login link to your email address: ", error.name); }) } return( <div className = "container"> <div className = "row justify-content-center"> <form className = "col-md-4 mt-3 pt-3 pb-3"> { "" !== notice && <div className = "alert alert-warning" role = "alert"> { notice } </div> } <div className = "form-floating mb-3"> <input type = "email" className = "form-control" id = "exampleInputEmail" placeholder = "[email protected]" value = { email } onChange = { (e) => setEmail(e.target.value) }></input> <label htmlFor = "exampleInputEmail" className = "form-label">Email address</label> </div> <div className = "d-grid"> <button type = "submit" className = "btn btn-primary pt-3 pb-3" onClick = {(e) => callSendSignInLinkToEmail(e)}>Submit</button> </div> </form> </div> </div> ) }
Login.jsx
。
捕获用户的电子邮件地址后, Login.jsx
表单会通过 Firebase 的sendSignInLinkToEmail
方法向他/她的地址发送一封包含登录链接的电子邮件。如果成功,用户会收到电子邮件已发送的通知。请注意, actionCodeSettings
对象作为参数传递给sendSignInLinkToEmail
方法,并包含用户单击通过电子邮件发送的登录链接时将路由到的 URL。在本例中,URL 映射到App.js
中指定的/confirm
路由。
Confirm.jsx
Firebase 的signInWithEmailLink
方法用于让单击登录链接的用户登录。正如您稍后将看到的,该方法采用email
参数,并且email
的值必须与用户通过登录表单登录时使用的电子邮件地址匹配。 Confirm.jsx
向用户提供一个表单来确认他/她的电子邮件地址,然后尝试登录用户。
在src
目录中创建一个新的Confirm.jsx
文件。
添加以下代码:
import { useState } from "react"; import { auth } from "./firebase"; import { isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth"; import { useNavigate } from "react-router-dom"; const Confirm = () => { const navigate = useNavigate(); const [email, setEmail] = useState(""); const [notice, setNotice] = useState(""); const callSignInWithEmailLink = (e) => { e.preventDefault(); if (isSignInWithEmailLink(auth, window.location.href)) { signInWithEmailLink(auth, email, window.location.href) .then(() => { navigate("/profile"); }) .catch((error) => { setNotice("An occurred during sign in: ", error.name); }) } } return( <div className = "container"> <div className = "row justify-content-center"> <form className = "col-md-4 mt-3 pt-3 pb-3"> { "" !== notice && <div className = "alert alert-warning" role = "alert"> { notice } </div> } <div className = "form-floating mb-3"> <input type = "email" className = "form-control" id = "exampleConfirmEmail" placeholder = "[email protected]" value = { email } onChange = { (e) => setEmail(e.target.value) }></input> <label htmlFor = "exampleConfirmEmail" className = "form-label">Please confirm your email address</label> </div> <div className = "d-grid"> <button type = "submit" className = "btn btn-primary pt-3 pb-3" onClick = {(e) => callSignInWithEmailLink(e)}>Confirm</button> </div> </form> </div> </div> ) } export default Confirm
Confirm.jsx
。
isSignInWithEmailLink
方法首先检查用户使用的登录链接是否有效。如果是,则调用signInWithEmailLink
方法来登录用户。重申一下,传递给signInWithEmailLink
方法的email
值必须与用户在登录表单中使用的电子邮件地址匹配。请注意,如果用户是新用户(即,这是他/她第一次登录),Firebase 将自动在 Firebase 身份验证存储中创建该用户。这是无密码身份验证提供的简化体验的另一个示例:自动处理新用户的帐户创建。
Profile.jsx
您将构建的最终组件是Profile.jsx
。用户通过Confirm.jsx
成功登录后将被路由到此组件。该路线欢迎用户提供电子邮件地址,并提供注销按钮。注销后,用户将被路由回Login
组件。
在src
目录中创建一个新的Profile.jsx
文件。
添加以下代码:
import { auth } from "./firebase"; import { signOut } from "firebase/auth"; import { useNavigate } from "react-router-dom"; const Profile = () => { const navigate = useNavigate(); const logoutUser = async (e) => { e.preventDefault(); await signOut(auth); navigate("/"); } return( <div className = "container"> <div className = "row justify-content-center"> <div className = "col-md-4 text-center"> <p>Welcome <em className = "text-decoration-underline">{ auth.currentUser.email }</em>. You are logged in!</p> <div className = "d-grid gap-2"> <button type = "submit" className = "btn btn-primary pt-3 pb-3" onClick = {(e) => logoutUser(e)}>Logout</button> </div> </div> </div> </div> ) } export default Profile
Profile.jsx
。启动 React 应用程序:
npm start
locahost:3000
。您应该看到Login
表单。
输入您要用于登录的电子邮件,然后单击“提交” 。如果提交成功,将显示一条通知,表明带有登录链接的电子邮件已发送到您的电子邮件地址。
Sign in to project-1078604952662
,其中 13 位数字序列代表 Firebase 项目的messagingSenderId
(请参阅本指南的步骤 3)。在下面的可选部分中,我将解释如何修改 Firebase 项目名称以在登录链接电子邮件中显示“用户友好”的名称。现在,打开登录链接电子邮件并单击登录链接。您将被转到Confirm
表单。
输入您在Confirm
表单上登录时使用的电子邮件地址。单击“确认” 。如果确认成功,您将被引导至Profile
页面。
单击Profile
页面上的“注销”按钮以注销。如果注销成功,您将返回到Login
表单。
上述步骤捕获了应用程序的工作流程。
您可以更改项目名称,以便 Firebase 发送的登录链接电子邮件显示“用户友好”的名称,而不是诸如project-1078604952662
之类的名称。登录您的Firebase 帐户并点击Go to Console 。
无密码身份验证似乎是应用程序开发人员中越来越流行的选择,这是可以理解的。除了无需管理密码的明显优势之外,还不需要电子邮件验证,因为发送登录链接的过程本身就是一种验证。
与先决项目一样,这里的实施是基础的。您可以考虑简单的增强功能,例如:
将特定电子邮件地址域(即常见的垃圾邮件域)阻止/列入黑名单。
本地存储用户在Login
页面输入的电子邮件地址,并在Confirm
页面检查该电子邮件地址是否存在。通过这种方法,如果用户在访问Login
页面的同一设备上单击登录链接,则他/她无需在Confirm
页面上再次输入他/她的电子邮件地址,因为该地址将被恢复来自本地存储。这提供了更加顺畅的用户体验。
您可以通过官方文档了解有关 Firebase 无密码身份验证的更多信息。