高级身份验证功能

1. 设置

获取源代码

在此 Codelab 中,您将从一个几乎完整的 Free Chat 示例应用版本开始,因此您需要首先克隆源代码:

$ git clone https://github.com/firebase/codelab-friendlychat-web --branch security

然后,进入 security-start 目录,您将在此 Codelab 中完成其余部分:

$ cd codelab-friendlychat-web/security-start

现在,安装依赖项,以便运行代码。如果您的互联网连接速度较慢,此过程可能需要一两分钟的时间:

$ npm install && (cd functions && npm install)

了解此代码库

security-solution/ 目录包含示例应用的完整代码。security-start 目录是您学习此 Codelab 的地方,其中缺少身份验证实现的一些重要部分。security-start/security-solution/ 中的主要文件和功能如下:

  • functions/index.js 包含 Cloud Functions 函数代码,您将在这里编写身份验证屏蔽函数。
  • public/ - 包含聊天应用的静态文件
  • public/scripts/main.js - 用于编译聊天应用的 JS 代码 (src/index.js)
  • src/firebase-config.js - 包含用于初始化聊天应用的 Firebase 配置对象
  • src/index.js - 您的聊天应用 JS 代码

获取 Firebase CLI

Emulator Suite 是 Firebase CLI(命令行界面)的一部分,可使用以下命令安装在您的计算机上:

$ npm install -g firebase-tools@latest

使用 webpack 构建 JavaScript,这将在 public/scripts/ 目录内创建 main.js。

webpack build

接下来,确认您拥有最新版本的 CLI。此 Codelab 适用于 11.14 或更高版本。

$ firebase --version
11.14.2

关联到您的 Firebase 项目

如果您还没有 Firebase 项目,请在 Firebase 控制台中创建一个新的 Firebase 项目。记下您选择的项目 ID,稍后需要用到。

现在,您需要将此代码关联到您的 Firebase 项目。首先运行以下命令登录 Firebase CLI:

$ firebase login

接下来,运行以下命令以创建项目别名。将 $YOUR_PROJECT_ID 替换为您的 Firebase 项目的 ID。

$ firebase use $YOUR_PROJECT_ID

现在,您可以运行应用了!

2. 运行模拟器

在本部分中,您将在本地运行应用。这意味着该启动 Emulator Suite 了。

启动模拟器

从 Codelab 源目录内部运行以下命令,启动模拟器:

$ firebase emulators:start

这将在 http://127.0.0.1:5170 提供您的应用,并在您做出更改时持续重建源代码。您只需在本地在浏览器中执行硬刷新 (ctrl-shift-r) 即可看到更改。

您应该会看到类似如下的输出内容:

i  emulators: Starting emulators: auth, functions, firestore, hosting, storage
✔  functions: Using node@16 from host.
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  hosting[demo-example]: Serving hosting files from: ./public
✔  hosting[demo-example]: Local server: http://127.0.0.1:5170
i  ui: Emulator UI logging to ui-debug.log
i  functions: Watching "[...]" for Cloud Functions...
✔  functions: Loaded functions definitions from source: beforecreated.
✔  functions[us-central1-beforecreated]: providers/cloud.auth/eventTypes/user.beforeCreate function initialized (http://127.0.0.1:5011/[...]/us-central1/beforecreated).
i  Running script: npm start
 
> security@1.0.0 start
> webpack --watch --progress
[...]
webpack 5.50.0 compiled with 1 warning in 990 ms

看到 All emulators ready 消息后,您就可以使用该应用了。

3. 实现 MFA

MFA 已在此代码库中部分实现。您需要添加该代码,以便先为用户注册 MFA,然后提示已注册 MFA 的用户进行第二重身份验证。

在您的编辑器中,打开 src/index.js 文件并找到 startEnrollMultiFactor() 方法。添加以下代码以设置 reCAPTCHA 验证程序,以防止手机滥用(reCAPTCHA 验证程序设置为不可见,且对用户不可见):

async function startEnrollMultiFactor(phoneNumber) {
  const recaptchaVerifier = new RecaptchaVerifier(
    "recaptcha",
    { size: "invisible" },
    getAuth()
  );

然后,找到 finishEnrollMultiFactor() 方法,并添加以下代码来注册第二重身份验证:

// Completes MFA enrollment once a verification code is obtained.
async function finishEnrollMultiFactor(verificationCode) {
  // Ask user for the verification code. Then:
  const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
  const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
 
  // Complete enrollment.
  await multiFactor(getAuth().currentUser)
    .enroll(multiFactorAssertion)
    .catch(function (error) {
      alert(`Error finishing second factor enrollment. ${error}`);
      throw error;
    });
  verificationId = null;
}

接下来,找到 signIn 函数并添加以下控制流,以提示已注册 MFA 的用户输入其第二重身份验证:

async function signIn() {
  // Sign in Firebase using popup auth and Google as the identity provider.
  var provider = new GoogleAuthProvider();
  await signInWithPopup(getAuth(), provider)
    .then(function (userCredential) {
      // User successfully signed in and is not enrolled with a second factor.
    })
    .catch(function (error) {
      if (error.code == "auth/multi-factor-auth-required") {
        multiFactorResolver = getMultiFactorResolver(getAuth(), error);
        displaySecondFactor(multiFactorResolver.hints);
      } else {
        alert(`Error signing in user. ${error}`);
      }
    });
}

该实现的其余部分(包括此处调用的函数)已经完成。要了解它们的运作方式,请浏览文件的其余部分。

4.尝试在模拟器中使用 MFA 登录

现在,试试 MFA 实现!确保您的模拟器仍在运行,并访问位于 localhost:5170 的本地托管应用。请尝试登录,当系统提示您提供 MFA 代码时,您会在终端窗口中看到 MFA 代码。

由于模拟器完全支持多因素身份验证,因此开发环境可以完全独立。

如需详细了解如何实现 MFA,请参阅我们的参考文档

5. 创建屏蔽函数

有些应用仅供特定用户群组使用。对于这些情况,您希望能够创建自定义要求,以便用户注册或登录您的应用。

这就是屏蔽函数的作用:一种创建自定义身份验证要求的方式。它们是 Cloud Functions 函数,但与大多数函数不同,它们会在用户尝试注册或登录时同步运行。

如需创建屏蔽函数,请在编辑器中打开 functions/index.js,然后找到被注释掉的 beforecreated 函数。

将其替换为以下代码,以仅允许域名为 example.com 的用户创建账号:

exports.beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  // Only users of a specific domain can sign up.
  if (!user.email || !user.email.endsWith("@example.com")) {
    throw new HttpsError("invalid-argument", "Unauthorized email");
  }
});

6. 在模拟器中试用屏蔽函数

如需试用屏蔽功能,请确保您的模拟器正在运行,并在 localhost:5170 的 Web 应用中退出帐号。

然后,尝试使用不以 example.com 结尾的电子邮件地址创建一个账号。屏蔽函数会阻止操作成功。

现在,请使用以 example.com 结尾的电子邮件地址重试。账号将成功创建。

借助屏蔽函数,您可以创建所需的任何身份验证限制。如需了解详情,请参阅参考文档

回顾

太棒了!您向 Web 应用添加了多重身份验证以帮助用户保障其帐号安全,然后您又创建了自定义要求,以便用户使用屏蔽函数进行注册。您绝对获得了一张 GIF 图片!

一张 GIF 图片,显示了办公室里人员在顶屋顶跳舞的 GIF