创建自定义电子邮件操作处理程序

某些用户管理操作(例如,更新用户的电子邮件地址和重置用户的密码)会导致向用户发送电子邮件。这些电子邮件会包含一些链接,收件人可以打开这些链接来完成或取消用户管理操作。默认情况下,用户管理电子邮件会链接到默认操作处理程序,该程序是由您的项目的 Firebase Hosting 网域中的某个网址托管的一个网页。

您也可以创建并托管一个自定义电子邮件操作处理程序,以进行自定义处理以及将该电子邮件操作处理程序与您的网站集成到一起。

下面的用户管理操作要求用户使用电子邮件操作处理程序来完成操作:

  • 重置密码
  • 撤消电子邮件地址更改 - 当用户更改其账号的主电子邮件地址时,Firebase 会向其旧电子邮件地址发送一封邮件,用户可在其中撤消该电子邮件地址更改。
  • 确认电子邮件地址

如需自定义您的 Firebase 项目的电子邮件操作处理程序,您必须创建并托管一个网页,该网页使用 Firebase JavaScript SDK 来验证请求的有效性并完成请求。然后,您必须自定义您的 Firebase 项目的电子邮件模板以关联到您的自定义操作处理程序。

创建电子邮件操作处理程序页面

  1. Firebase 在生成用户管理电子邮件时,会将多个查询参数添加到您的操作处理程序网址。例如:

    https://example.com/usermgmt?mode=resetPassword&oobCode=ABC123&apiKey=AIzaSy...&lang=fr

    这些参数指明了用户正在完成的用户管理任务。您的电子邮件操作处理程序页面必须处理以下查询参数:

    参数
    模式

    要完成的用户管理操作。可以是下列值之一:

    • resetPassword
    • recoverEmail
    • verifyEmail
    oobCode 一个一次性代码,用于识别和验证请求
    apiKey 您的 Firebase 项目的 API 密钥(提供该密钥是为了方便您进行操作)
    continueUrl 这是一个可选网址,提供了一种通过网址将状态传回应用的方法。这与密码重置和电子邮件验证模式相关。当发送密码重置电子邮件或验证电子邮件时,需要为 ActionCodeSettings 对象指定接续网址,才能达到相应目的。这样用户便可以在执行电子邮件操作之后从他们原先所在的位置继续。
    lang

    这是表示用户语言区域的可选 BCP47 语言标记(例如 fr)。您可以使用此值为用户提供本地化的电子邮件操作处理程序页面。

    您可以通过 Firebase 控制台进行本地化设置,也可以在触发电子邮件操作之前调用相应的客户端 API 进行动态设置。例如,使用 JavaScript:firebase.auth().languageCode = 'fr';

    为了获得一致的用户体验,请确保电子邮件操作处理程序采用与电子邮件模板相同的本地化语言。

    以下示例显示了在基于浏览器的处理程序中如何处理查询参数。(您也可以使用相似的逻辑,在 Node.js 应用中实现该处理程序。)

    Web

    import { initializeApp } from "firebase/app";
    import { getAuth } from "firebase/auth";
    
    document.addEventListener('DOMContentLoaded', () => {
      // TODO: Implement getParameterByName()
    
      // Get the action to complete.
      const mode = getParameterByName('mode');
      // Get the one-time code from the query parameter.
      const actionCode = getParameterByName('oobCode');
      // (Optional) Get the continue URL from the query parameter if available.
      const continueUrl = getParameterByName('continueUrl');
      // (Optional) Get the language code if available.
      const lang = getParameterByName('lang') || 'en';
    
      // Configure the Firebase SDK.
      // This is the minimum configuration required for the API to be used.
      const config = {
        'apiKey': "YOUR_API_KEY" // Copy this key from the web initialization
                                 // snippet found in the Firebase console.
      };
      const app = initializeApp(config);
      const auth = getAuth(app);
    
      // Handle the user management action.
      switch (mode) {
        case 'resetPassword':
          // Display reset password handler and UI.
          handleResetPassword(auth, actionCode, continueUrl, lang);
          break;
        case 'recoverEmail':
          // Display email recovery handler and UI.
          handleRecoverEmail(auth, actionCode, lang);
          break;
        case 'verifyEmail':
          // Display email verification handler and UI.
          handleVerifyEmail(auth, actionCode, continueUrl, lang);
          break;
        default:
          // Error: invalid mode.
      }
    }, false);

    Web

    document.addEventListener('DOMContentLoaded', () => {
      // TODO: Implement getParameterByName()
    
      // Get the action to complete.
      var mode = getParameterByName('mode');
      // Get the one-time code from the query parameter.
      var actionCode = getParameterByName('oobCode');
      // (Optional) Get the continue URL from the query parameter if available.
      var continueUrl = getParameterByName('continueUrl');
      // (Optional) Get the language code if available.
      var lang = getParameterByName('lang') || 'en';
    
      // Configure the Firebase SDK.
      // This is the minimum configuration required for the API to be used.
      var config = {
        'apiKey': "YOU_API_KEY" // Copy this key from the web initialization
                                // snippet found in the Firebase console.
      };
      var app = firebase.initializeApp(config);
      var auth = app.auth();
    
      // Handle the user management action.
      switch (mode) {
        case 'resetPassword':
          // Display reset password handler and UI.
          handleResetPassword(auth, actionCode, continueUrl, lang);
          break;
        case 'recoverEmail':
          // Display email recovery handler and UI.
          handleRecoverEmail(auth, actionCode, lang);
          break;
        case 'verifyEmail':
          // Display email verification handler and UI.
          handleVerifyEmail(auth, actionCode, continueUrl, lang);
          break;
        default:
          // Error: invalid mode.
      }
    }, false);
  2. 处理密码重置请求,方法如下:首先,使用 verifyPasswordResetCode 验证操作代码;然后,从用户处获取一个新密码并将其传递给 confirmPasswordReset。例如:

    Web

    import { verifyPasswordResetCode, confirmPasswordReset } from "firebase/auth";
    
    function handleResetPassword(auth, actionCode, continueUrl, lang) {
      // Localize the UI to the selected language as determined by the lang
      // parameter.
    
      // Verify the password reset code is valid.
      verifyPasswordResetCode(auth, actionCode).then((email) => {
        const accountEmail = email;
    
        // TODO: Show the reset screen with the user's email and ask the user for
        // the new password.
        const newPassword = "...";
    
        // Save the new password.
        confirmPasswordReset(auth, actionCode, newPassword).then((resp) => {
          // Password reset has been confirmed and new password updated.
    
          // TODO: Display a link back to the app, or sign-in the user directly
          // if the page belongs to the same domain as the app:
          // auth.signInWithEmailAndPassword(accountEmail, newPassword);
    
          // TODO: If a continue URL is available, display a button which on
          // click redirects the user back to the app via continueUrl with
          // additional state determined from that URL's parameters.
        }).catch((error) => {
          // Error occurred during confirmation. The code might have expired or the
          // password is too weak.
        });
      }).catch((error) => {
        // Invalid or expired action code. Ask user to try to reset the password
        // again.
      });
    }

    Web

    function handleResetPassword(auth, actionCode, continueUrl, lang) {
      // Localize the UI to the selected language as determined by the lang
      // parameter.
    
      // Verify the password reset code is valid.
      auth.verifyPasswordResetCode(actionCode).then((email) => {
        var accountEmail = email;
    
        // TODO: Show the reset screen with the user's email and ask the user for
        // the new password.
        var newPassword = "...";
    
        // Save the new password.
        auth.confirmPasswordReset(actionCode, newPassword).then((resp) => {
          // Password reset has been confirmed and new password updated.
    
          // TODO: Display a link back to the app, or sign-in the user directly
          // if the page belongs to the same domain as the app:
          // auth.signInWithEmailAndPassword(accountEmail, newPassword);
    
          // TODO: If a continue URL is available, display a button which on
          // click redirects the user back to the app via continueUrl with
          // additional state determined from that URL's parameters.
        }).catch((error) => {
          // Error occurred during confirmation. The code might have expired or the
          // password is too weak.
        });
      }).catch((error) => {
        // Invalid or expired action code. Ask user to try to reset the password
        // again.
      });
    }
  3. 处理电子邮件地址更改撤消,方法如下:首先,使用 checkActionCode 验证操作代码;然后,使用 applyActionCode 恢复用户的电子邮件地址。例如:

    Web

    import { checkActionCode, applyActionCode, sendPasswordResetEmail } from "firebase/auth";
    
    function handleRecoverEmail(auth, actionCode, lang) {
      // Localize the UI to the selected language as determined by the lang
      // parameter.
      let restoredEmail = null;
      // Confirm the action code is valid.
      checkActionCode(auth, actionCode).then((info) => {
        // Get the restored email address.
        restoredEmail = info['data']['email'];
    
        // Revert to the old email.
        return applyActionCode(auth, actionCode);
      }).then(() => {
        // Account email reverted to restoredEmail
    
        // TODO: Display a confirmation message to the user.
    
        // You might also want to give the user the option to reset their password
        // in case the account was compromised:
        sendPasswordResetEmail(auth, restoredEmail).then(() => {
          // Password reset confirmation sent. Ask user to check their email.
        }).catch((error) => {
          // Error encountered while sending password reset code.
        });
      }).catch((error) => {
        // Invalid code.
      });
    }

    Web

    function handleRecoverEmail(auth, actionCode, lang) {
      // Localize the UI to the selected language as determined by the lang
      // parameter.
      var restoredEmail = null;
      // Confirm the action code is valid.
      auth.checkActionCode(actionCode).then((info) => {
        // Get the restored email address.
        restoredEmail = info['data']['email'];
    
        // Revert to the old email.
        return auth.applyActionCode(actionCode);
      }).then(() => {
        // Account email reverted to restoredEmail
    
        // TODO: Display a confirmation message to the user.
    
        // You might also want to give the user the option to reset their password
        // in case the account was compromised:
        auth.sendPasswordResetEmail(restoredEmail).then(() => {
          // Password reset confirmation sent. Ask user to check their email.
        }).catch((error) => {
          // Error encountered while sending password reset code.
        });
      }).catch((error) => {
        // Invalid code.
      });
    }
  4. 调用 applyActionCode 处理电子邮件地址验证。例如:

    Web

    function handleVerifyEmail(auth, actionCode, continueUrl, lang) {
      // Localize the UI to the selected language as determined by the lang
      // parameter.
      // Try to apply the email verification code.
      applyActionCode(auth, actionCode).then((resp) => {
        // Email address has been verified.
    
        // TODO: Display a confirmation message to the user.
        // You could also provide the user with a link back to the app.
    
        // TODO: If a continue URL is available, display a button which on
        // click redirects the user back to the app via continueUrl with
        // additional state determined from that URL's parameters.
      }).catch((error) => {
        // Code is invalid or expired. Ask the user to verify their email address
        // again.
      });
    }

    Web

    function handleVerifyEmail(auth, actionCode, continueUrl, lang) {
      // Localize the UI to the selected language as determined by the lang
      // parameter.
      // Try to apply the email verification code.
      auth.applyActionCode(actionCode).then((resp) => {
        // Email address has been verified.
    
        // TODO: Display a confirmation message to the user.
        // You could also provide the user with a link back to the app.
    
        // TODO: If a continue URL is available, display a button which on
        // click redirects the user back to the app via continueUrl with
        // additional state determined from that URL's parameters.
      }).catch((error) => {
        // Code is invalid or expired. Ask the user to verify their email address
        // again.
      });
    }
  5. 将页面托管于某处,例如使用 Firebase Hosting

接下来,您必须对自己的 Firebase 项目进行配置,将其关联到用户管理电子邮件中您的自定义电子邮件操作处理程序。

如需将您的 Firebase 项目配置为使用您的自定义电子邮件操作处理程序,请按以下步骤操作:

  1. Firebase 控制台中打开您的项目。
  2. 身份验证部分中打开电子邮件模板页面。
  3. 在任一电子邮件类型条目中,点击铅笔图标以编辑电子邮件模板。
  4. 点击自定义操作网址,然后指定您的自定义电子邮件操作处理程序的网址。

在您保存该网址之后,您的所有 Firebase 项目的电子邮件模板将都可以使用该网址。