介绍
本指南是如何使用 React Web 应用程序设置 Firebase 身份验证的补充。在该指南的介绍中,我注意到 Firebase 提供了除基本电子邮件/密码身份验证之外的其他身份验证方案。这些替代方案之一是无密码身份验证。
在构建应用程序时,无密码身份验证是一个有吸引力的选择。它简化了用户体验,因为您的用户无需记住他们的密码,因此也不必担心丢失密码。它还简化了开发体验,因为无需设计任何密码捕获或管理逻辑。
在本指南中,您将构建一个简单的登录/确认/个人资料/注销工作流程来实现 Firebase 的无密码身份验证。
在你开始之前
Google 设置了各种 Firebase 身份验证限制。如果您使用免费的Spark计划,请注意,您每天最多只能发送 5 封登录链接电子邮件。虽然 Spark 计划可能足以满足测试目的,但您需要升级到即用即付Blaze计划才能超过此限制。
先决条件
在本指南中,我将参考如何使用 React Web 应用程序设置 Firebase 身份验证指南作为先决条件指南,并将关联的项目作为先决项目。
要完成本指南,您需要:
- 已完成先决条件指南,包括所有先决条件。
第 1 步 - 在您的 Firebase 项目中启用无密码身份验证
在先决条件指南中,您创建了一个用于基本电子邮件/密码身份验证的新 Firebase 项目。现在,您将为同一项目启用无密码身份验证。登录您的Firebase 帐户并点击Go to Console 。
单击 Firebase 项目仪表板上列出的身份验证项目。本指南使用项目名称
my-auth-test
。单击左侧面板菜单中的身份验证。
单击主窗口中的登录方法选项卡。
根据您在先决条件指南中的工作,登录提供程序表应该已在提供程序列下显示电子邮件/密码,状态为Enabled 。单击铅笔图标打开电子邮件/密码提供商的配置面板。
单击开关以启用电子邮件链接(无密码登录) 。
单击“保存” 。
您的 Firebase 项目现已配置为支持无密码身份验证。
第 2 步 - 创建一个新的 React 项目并安装包
步骤 2a - 创建一个新的 React 项目
使用您所需的应用程序名称创建一个新的 React 项目。本指南使用passwordless-auth
。
npx create-react-app passwordless-auth
步骤 2b - 安装软件包
本指南需要安装 3 个 Node.js 包:
- Firebase :Firebase SDK。
- React Router DOM :用于路由。
- Bootstrap :用于样式。
通过npm
安装上述三个软件包:
npm install firebase
npm install react-router-dom
npm install bootstrap
第 3 步 - 从先决项目复制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选项应该已被选择。您的项目配置值将在显示的代码块中列出。
第 4 步 - 构建 React 应用程序
步骤 4a - React 应用程序组件概述
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
步骤 4b - 清理 React 项目模板并从必备项目中复制文件
- 您可以按照先决条件指南的步骤 5b.1 中所述从 React 项目模板中删除相同的文件。从 React 项目中删除以下文件:
-
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
步骤 4c - 构建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
组件再次成为应用程序的主路由,就像先决项目一样。
步骤 4d - 构建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
路由。
步骤 4e - 构建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 身份验证存储中创建该用户。这是无密码身份验证提供的简化体验的另一个示例:自动处理新用户的帐户创建。
步骤 4f - 构建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
。
第 5 步 - 测试应用程序
启动 React 应用程序:
npm start
- 如果您的浏览器没有自动启动,请在浏览器中导航至
locahost:3000
。您应该看到Login
表单。
输入您要用于登录的电子邮件,然后单击“提交” 。如果提交成功,将显示一条通知,表明带有登录链接的电子邮件已发送到您的电子邮件地址。
- 登录您的电子邮件帐户并查找 Firebase 登录链接电子邮件。它的主题行应类似于
Sign in to project-1078604952662
,其中 13 位数字序列代表 Firebase 项目的messagingSenderId
(请参阅本指南的步骤 3)。在下面的可选部分中,我将解释如何修改 Firebase 项目名称以在登录链接电子邮件中显示“用户友好”的名称。现在,打开登录链接电子邮件并单击登录链接。您将被转到Confirm
表单。
输入您在
Confirm
表单上登录时使用的电子邮件地址。单击“确认” 。如果确认成功,您将被引导至Profile
页面。
单击
Profile
页面上的“注销”按钮以注销。如果注销成功,您将返回到Login
表单。
上述步骤捕获了应用程序的工作流程。
可选:修改您的项目名称
您可以更改项目名称,以便 Firebase 发送的登录链接电子邮件显示“用户友好”的名称,而不是诸如project-1078604952662
之类的名称。登录您的Firebase 帐户并点击Go to Console 。
- 单击 Firebase 项目仪表板上列出的身份验证项目。
- 单击左侧面板菜单中“项目概述”旁边的齿轮图标。应已选择“常规”选项卡。
- 向下滚动到“您的项目”部分中的“面向公众的名称”选项。
- 单击铅笔图标并根据需要修改项目名称。
- 单击“保存” 。您的登录链接电子邮件现在将显示更新的项目名称。
结论和后续步骤
无密码身份验证似乎是应用程序开发人员中越来越流行的选择,这是可以理解的。除了无需管理密码的明显优势之外,还不需要电子邮件验证,因为发送登录链接的过程本身就是一种验证。
与先决项目一样,这里的实施是基础的。您可以考虑简单的增强功能,例如:
将特定电子邮件地址域(即常见的垃圾邮件域)阻止/列入黑名单。
本地存储用户在
Login
页面输入的电子邮件地址,并在Confirm
页面检查该电子邮件地址是否存在。通过这种方法,如果用户在访问Login
页面的同一设备上单击登录链接,则他/她无需在Confirm
页面上再次输入他/她的电子邮件地址,因为该地址将被恢复来自本地存储。这提供了更加顺畅的用户体验。
您可以通过官方文档了解有关 Firebase 无密码身份验证的更多信息。