Как создать обработчики действий с электронной почтой

Некоторые действия по управлению пользователями, например изменение адреса электронной почты или сброс пароля, приводят к отправке пользователю электронных писем. В этих письмах есть ссылки, по которым получатели могут перейти, чтобы завершить или отменить действие по управлению пользователем. По умолчанию в письмах об управлении пользователями есть ссылки на обработчик действий по умолчанию – веб-страницу, размещенную по URL в домене Firebase Hosting вашего проекта.

Вместо этого вы можете создать и разместить специальный обработчик действий с электронной почтой, чтобы выполнять собственные операции и интегрировать обработчик действий с электронной почтой с вашим сайтом.

Для выполнения следующих действий по управлению пользователями необходимо использовать обработчик действий электронной почты:

  • Как сбросить пароль
  • Отмена изменения адреса электронной почты – когда пользователи меняют основной адрес электронной почты в аккаунте, Firebase отправляет на старый адрес письмо, позволяющее отменить изменение.
  • Как подтвердить адрес электронной почты

Чтобы настроить обработчик действий с электронной почтой в проекте Firebase, создайте и разместите веб-страницу, на которой с помощью Firebase JavaScript SDK проверяется действительность запроса и выполняется запрос. Затем вам нужно будет настроить шаблоны электронных писем в проекте Firebase, чтобы они ссылались на ваш обработчик пользовательских действий.

Как создать страницу обработчика действий с электронной почтой

  1. Когда Firebase создает электронные письма для управления пользователями, в URL обработчика действий добавляются несколько параметров запроса. Пример:

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

    Эти параметры указывают, какую задачу по управлению пользователями выполняет пользователь. Страница обработчика действий с электронной почтой должна обрабатывать следующие параметры запроса:

    Параметры
    режим

    Действие по управлению пользователями, которое нужно выполнить. Может принимать одно из следующих значений:

    • resetPassword
    • recoverEmail
    • verifyEmail
    oobCode Одноразовый код, используемый для идентификации и проверки запроса.
    apiKey Ключ API проекта Firebase (для удобства)
    continueUrl Это необязательный URL, который позволяет передавать данные обратно в приложение через URL. Это относится к режимам сброса пароля и подтверждения адреса электронной почты. При отправке письма для сброса пароля или письма с подтверждением необходимо указать объект ActionCodeSettings с URL продолжения, чтобы эта функция была доступна. Это позволяет пользователю продолжить работу с того места, на котором он остановился после выполнения действия в письме.
    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 Authentication управляет ключом API, встроенным в сгенерированные ссылки на действия, например параметром apiKey в URL. Если вы смените ключи API проекта или удалите ключ, изначально связанный с аутентификацией Firebase, пользователи могут получать ссылки с недействительными ключами, что приведет к ошибкам auth/invalid-api-key.

Чтобы устранить эту проблему:

  • Если вы используете Firebase Hosting, ключ API может быть сохранен в кеше init.json. Разверните новую версию сайта хостинга (firebase deploy --only hosting), чтобы принудительно заставить сервис метаданных на сервере сгенерировать конфигурацию с активным ключом API.
  • Если нужно обновить сам ключ API для внутреннего сервиса. Поскольку сопоставление ключа API для внутреннего сервиса нельзя изменить напрямую с помощью консоли или SDK, вам нужно обратиться в службу поддержки и запросить обновление конфигурации аутентификации для внутреннего сервиса в вашем проекте.

Затем вам нужно настроить проект Firebase, чтобы он ссылался на ваш обработчик действий с электронной почтой в письмах для управления пользователями.

Чтобы настроить проект Firebase для использования собственного обработчика действий с электронной почтой:

  1. В консоли Firebase перейдите на вкладку Безопасность > Аутентификация > Шаблоны.

  2. В любом из разделов Типы электронных писем нажмите на значок карандаша, чтобы изменить шаблон письма.

  3. Нажмите Настроить URL действия и укажите URL обработчика действий с электронной почтой.

После сохранения URL он будет использоваться во всех шаблонах электронных писем проекта Firebase.