Аутентификация с помощью Firebase с использованием ссылки электронной почты в JavaScript

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

Вход по электронной почте имеет множество преимуществ:

  • Регистрация и вход с низким коэффициентом трения.
  • Меньший риск повторного использования пароля в разных приложениях, что может подорвать безопасность даже хорошо подобранных паролей.
  • Возможность аутентифицировать пользователя, а также подтверждать, что пользователь является законным владельцем адреса электронной почты.
  • Для входа в систему пользователю требуется только доступная учетная запись электронной почты. Владение номером телефона или учетной записью в социальных сетях не требуется.
  • Пользователь может безопасно войти в систему без необходимости вводить (или запоминать) пароль, что может быть громоздко на мобильном устройстве.
  • Существующий пользователь, который ранее входил в систему с помощью идентификатора электронной почты (пароль или федерация), может быть обновлен для входа только с помощью электронной почты. Например, пользователь, который забыл свой пароль, все еще может войти в систему, не сбрасывая пароль.

Прежде чем вы начнете

Если вы еще этого не сделали, скопируйте фрагмент кода инициализации из консоли Firebase в свой проект, как описано в разделе Добавление Firebase в ваш проект JavaScript .

Чтобы входить в систему по ссылке электронной почты, вы должны сначала включить поставщика электронной почты и метод входа по ссылке электронной почты для своего проекта Firebase:

  1. В консоли Firebase откройте раздел Auth .
  2. На вкладке «Метод входа» включите поставщика электронной почты/пароля . Обратите внимание, что для входа по электронной почте необходимо включить вход по электронной почте/паролю.
  3. В том же разделе включите метод входа по ссылке электронной почты (вход без пароля) .
  4. Нажмите Сохранить .

Чтобы инициировать процесс аутентификации, предоставьте пользователю интерфейс, который предлагает пользователю указать свой адрес электронной почты, а затем вызовите sendSignInLinkToEmail чтобы запросить, чтобы Firebase отправила ссылку для аутентификации на электронную почту пользователя.

  1. Создайте объект ActionCodeSettings , который предоставит Firebase инструкции по созданию ссылки электронной почты. Задайте следующие поля:

    • url : глубокая ссылка для встраивания и любое дополнительное состояние, которое необходимо передать. Домен ссылки должен быть добавлен в список авторизованных доменов консоли Firebase, который можно найти, перейдя на вкладку «Метод входа» (Аутентификация -> Метод входа).
    • android и ios : приложения, которые будут использоваться при открытии ссылки для входа на устройстве Android или Apple. Узнайте больше о том, как настроить динамические ссылки Firebase для открытия ссылок действий в электронной почте через мобильные приложения.
    • handleCodeInApp : установите значение true. Операция входа всегда должна выполняться в приложении, в отличие от других внеплановых действий с электронной почтой (сброс пароля и проверка электронной почты). Это связано с тем, что в конце потока ожидается, что пользователь войдет в систему, а его состояние аутентификации сохранится в приложении.
    • dynamicLinkDomain : если для проекта определено несколько настраиваемых доменов динамической ссылки, укажите, какой из них использовать, когда ссылка должна быть открыта через указанное мобильное приложение (например, example.page.link ). В противном случае автоматически выбирается первый домен.

      Web modular API

      const actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

      Web namespaced API

      var actionCodeSettings = {
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be in the authorized domains list in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true.
        handleCodeInApp: true,
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        dynamicLinkDomain: 'example.page.link'
      };

    Чтобы узнать больше о ActionCodeSettings, обратитесь к разделу Состояние передачи в действиях по электронной почте .

  2. Спросите у пользователя адрес электронной почты.

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

    Web modular API

    import { getAuth, sendSignInLinkToEmail } from "firebase/auth";
    
    const auth = getAuth();
    sendSignInLinkToEmail(auth, email, actionCodeSettings)
      .then(() => {
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        const errorCode = error.code;
        const errorMessage = error.message;
        // ...
      });

    Web namespaced API

    firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
      .then(() => {
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        window.localStorage.setItem('emailForSignIn', email);
        // ...
      })
      .catch((error) => {
        var errorCode = error.code;
        var errorMessage = error.message;
        // ...
      });

Вопросы безопасности

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

Вы можете оптимизировать этот процесс для пользователей, которые открывают ссылку для входа на том же устройстве, на котором они запрашивают ссылку, сохраняя их адрес электронной почты локально (например, с помощью localStorage или файлов cookie) при отправке электронного письма для входа. Затем используйте этот адрес для завершения потока. Не передавайте адрес электронной почты пользователя в параметрах URL-адреса перенаправления и не используйте его повторно, поскольку это может привести к внедрению сеансовых инъекций.

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

Также убедитесь, что вы используете URL-адрес HTTPS в рабочей среде, чтобы избежать потенциального перехвата вашей ссылки промежуточными серверами.

Завершение входа на веб-страницу

Формат прямой ссылки электронной почты такой же, как и формат, используемый для внешних действий электронной почты (подтверждение электронной почты, сброс пароля и отзыв изменения электронной почты). Firebase Auth упрощает эту проверку, предоставляя API isSignInWithEmailLink для проверки того, является ли ссылка ссылкой для входа по электронной почте.

Чтобы завершить вход на целевой странице, вызовите signInWithEmailLink , указав адрес электронной почты пользователя и фактическую ссылку на адрес электронной почты, содержащую одноразовый код.

Web modular API

import { getAuth, isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth";

// Confirm the link is a sign-in with email link.
const auth = getAuth();
if (isSignInWithEmailLink(auth, window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  let email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  signInWithEmailLink(auth, email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Web namespaced API

// Confirm the link is a sign-in with email link.
if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
  // Additional state parameters can also be passed via URL.
  // This can be used to continue the user's intended action before triggering
  // the sign-in operation.
  // Get the email if available. This should be available if the user completes
  // the flow on the same device where they started it.
  var email = window.localStorage.getItem('emailForSignIn');
  if (!email) {
    // User opened the link on a different device. To prevent session fixation
    // attacks, ask the user to provide the associated email again. For example:
    email = window.prompt('Please provide your email for confirmation');
  }
  // The client SDK will parse the code from the link for you.
  firebase.auth().signInWithEmailLink(email, window.location.href)
    .then((result) => {
      // Clear email from storage.
      window.localStorage.removeItem('emailForSignIn');
      // You can access the new user via result.user
      // Additional user info profile not available via:
      // result.additionalUserInfo.profile == null
      // You can check if the user is new or existing:
      // result.additionalUserInfo.isNewUser
    })
    .catch((error) => {
      // Some error occurred, you can inspect the code: error.code
      // Common errors could be invalid email and invalid or expired OTPs.
    });
}

Завершение входа в мобильное приложение

Аутентификация Firebase использует динамические ссылки Firebase для отправки ссылки по электронной почте на мобильное устройство. Для завершения входа через мобильное приложение приложение должно быть настроено на обнаружение входящей ссылки на приложение, анализ базовой глубокой ссылки, а затем завершение входа, как это делается через веб-поток.

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

Чтобы узнать больше о том, как обрабатывать вход с помощью ссылки электронной почты в приложении Apple, обратитесь к руководству по платформам Apple .

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

Разница будет во второй половине операции:

Web modular API

import { getAuth, linkWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
const auth = getAuth();
linkWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web namespaced API

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Link the credential to the current user.
firebase.auth().currentUser.linkWithCredential(credential)
  .then((usercred) => {
    // The provider is now successfully linked.
    // The phone user can now sign in with their phone number or email.
  })
  .catch((error) => {
    // Some error occurred.
  });

Это также можно использовать для повторной аутентификации пользователя ссылки электронной почты перед выполнением конфиденциальной операции.

Web modular API

import { getAuth, reauthenticateWithCredential, EmailAuthProvider } from "firebase/auth";

// Construct the email link credential from the current URL.
const credential = EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
const auth = getAuth();
reauthenticateWithCredential(auth.currentUser, credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

Web namespaced API

// Construct the email link credential from the current URL.
var credential = firebase.auth.EmailAuthProvider.credentialWithLink(
  email, window.location.href);

// Re-authenticate the user with this credential.
firebase.auth().currentUser.reauthenticateWithCredential(credential)
  .then((usercred) => {
    // The user is now successfully re-authenticated and can execute sensitive
    // operations.
  })
  .catch((error) => {
    // Some error occurred.
  });

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

Если вы поддерживаете вход на основе пароля и ссылки с помощью электронной почты, чтобы различать метод входа для пользователя с паролем/ссылкой, используйте fetchSignInMethodsForEmail . Это полезно для потоков с идентификатором в первую очередь, когда пользователя сначала просят указать адрес электронной почты, а затем предлагают метод входа:

Web modular API

import { getAuth, fetchSignInMethodsForEmail, EmailAuthProvider} from "firebase/auth";

// After asking the user for their email.
const email = window.prompt('Please provide your email');

const auth = getAuth();
fetchSignInMethodsForEmail(auth, email)
  .then((signInMethods) => {
    // This returns the same array as fetchProvidersForEmail but for email
    // provider identified by 'password' string, signInMethods would contain 2
    // different strings:
    // 'emailLink' if the user previously signed in with an email/link
    // 'password' if the user has a password.
    // A user could have both.
    if (signInMethods.indexOf(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/password.
    }
    if (signInMethods.indexOf(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/link.
    }
  })
  .catch((error) => {
    // Some error occurred, you can inspect the code: error.code
  });

Web namespaced API

// After asking the user for their email.
var email = window.prompt('Please provide your email');
firebase.auth().fetchSignInMethodsForEmail(email)
  .then((signInMethods) => {
    // This returns the same array as fetchProvidersForEmail but for email
    // provider identified by 'password' string, signInMethods would contain 2
    // different strings:
    // 'emailLink' if the user previously signed in with an email/link
    // 'password' if the user has a password.
    // A user could have both.
    if (signInMethods.indexOf(
            firebase.auth.EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/password.
    }
    if (signInMethods.indexOf(
            firebase.auth.EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD) != -1) {
      // User can sign in with email/link.
    }
  })
  .catch((error) => {
    // Some error occurred, you can inspect the code: error.code
  });

Как описано выше, адрес электронной почты/пароль и адрес электронной почты/ссылка считаются одним и тем же firebase.auth.EmailAuthProvider (один и тот же PROVIDER_ID ) с разными методами входа.

Следующие шаги

После того, как пользователь входит в систему в первый раз, создается новая учетная запись пользователя и связывается с учетными данными, т. е. с именем пользователя и паролем, номером телефона или информацией о поставщике проверки подлинности, с которыми пользователь вошел в систему. Эта новая учетная запись хранится как часть вашего проекта Firebase и может использоваться для идентификации пользователя во всех приложениях вашего проекта, независимо от того, как пользователь входит в систему.

  • В ваших приложениях рекомендуемым способом узнать статус аутентификации вашего пользователя является установка наблюдателя на объект Auth . Затем вы можете получить основную информацию о профиле пользователя из объекта User . См. Управление пользователями .

  • В правилах безопасности базы данных Firebase Realtime и облачного хранилища вы можете получить уникальный идентификатор пользователя, вошедшего в систему, из переменной auth и использовать его для управления тем, к каким данным пользователь может получить доступ.

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

Чтобы выйти из системы, вызовите signOut :

Web modular API

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web namespaced API

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});