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

Вы можете использовать Firebase Authentication, чтобы войти в аккаунт пользователя, отправив SMS-сообщение на его телефон. Пользователь входит в аккаунт с помощью одноразового кода из SMS.

Самый простой способ добавить в приложение вход с помощью номера телефона – использовать FirebaseUI. В этот пакет входит готовый виджет для входа, который поддерживает вход с помощью номера телефона, пароля и федеративный вход. В этом документе рассказывается, как реализовать вход с номером телефона с помощью Firebase SDK.

Подготовка

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

Проблемы с безопасностью

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

Если в вашем приложении для входа используется номер телефона, предложите пользователям более безопасные способы входа и расскажите о рисках, связанных с использованием номера телефона.

Как включить вход с помощью номера телефона в проекте Firebase

Чтобы пользователи могли входить в аккаунт с помощью SMS, сначала включите в проекте Firebase метод входа с номером телефона:

  1. В консоли Firebase выберите Безопасность > Аутентификация.
  2. На вкладке Sign-in method (Способ входа) включите поставщика для входа с помощью Phone (Телефона).
  3. Настройте правило для регионов, в которые вы хотите разрешить или запретить отправку SMS. Правило для региона SMS помогает защитить приложения от злоупотреблений с помощью SMS. Для новых проектов политика по умолчанию не разрешает использование регионов.
    1. В консоли Firebase выберите Безопасность > Аутентификация > вкладка Настройки.
    2. В разделе Правила в отношении регионов хранения данных для SMS настройте правила в отношении регионов хранения данных для SMS.
  4. Если вы этого ещё не сделали, авторизуйте домен приложения:
    1. В консоли Firebase выберите Безопасность > Аутентификация > вкладка Настройки.
    2. В разделе Авторизованные домены нажмите Добавить домен и добавьте свой домен.

    Обратите внимание, что localhost нельзя использовать в качестве размещенного домена для аутентификации по номеру телефона.

Как настроить проверку reCAPTCHA

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

Вам не нужно настраивать клиент reCAPTCHA вручную. Когда вы используете объект RecaptchaVerifier из Firebase SDK, Firebase автоматически создает и обрабатывает все необходимые клиентские ключи и секреты.

Объект RecaptchaVerifier поддерживает невидимую reCAPTCHA, которая часто может проверить пользователя без его участия, а также виджет reCAPTCHA, который всегда требует действий пользователя для успешного завершения.

Чтобы локализовать reCAPTCHA с учетом предпочтений пользователя, обновите код языка в экземпляре Auth до отрисовки reCAPTCHA. Указанная выше локализация также будет применяться к SMS-сообщению с кодом подтверждения, отправленному пользователю.

Web

import { getAuth } from "firebase/auth";

const auth = getAuth();
auth.languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// auth.useDeviceLanguage();

Web

firebase.auth().languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// firebase.auth().useDeviceLanguage();

Как использовать невидимую проверку reCAPTCHA

Чтобы использовать невидимую reCAPTCHA, создайте объект RecaptchaVerifier, задав для параметра size значение invisible и указав идентификатор кнопки, которая отправляет форму входа. Пример:

Web

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

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

Как использовать виджет reCAPTCHA

Чтобы использовать видимый виджет reCAPTCHA, создайте на странице элемент, который будет содержать виджет, а затем создайте объект RecaptchaVerifier, указав идентификатор контейнера. Пример:

Web

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

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');

Как задать параметры reCAPTCHA (необязательно)

Вы можете задать функции обратного вызова для объекта RecaptchaVerifier, которые будут вызываться, когда пользователь решит reCAPTCHA или когда срок действия reCAPTCHA истечет до того, как пользователь отправит форму:

Web

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

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

Web

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

Как предварительно отрисовать reCAPTCHA (необязательно)

Если вы хотите предварительно отрисовать reCAPTCHA до отправки запроса на вход, вызовите render:

Web

recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

Web

recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

После того как render будет разрешен, вы получите идентификатор виджета reCAPTCHA, который можно использовать для вызовов API reCAPTCHA:

Web

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

Web

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

Отправить код подтверждения на телефон пользователя.

Чтобы начать вход с номером телефона, покажите пользователю интерфейс, в котором он сможет ввести свой номер, а затем вызовите метод signInWithPhoneNumber, чтобы Firebase отправил код аутентификации на телефон пользователя по SMS:

  1. Получить номер телефона пользователя.

    Требования законодательства могут различаться, но мы рекомендуем сообщать пользователям, что при входе с помощью номера телефона они могут получить SMS с кодом подтверждения, за которое будет взиматься стандартная плата.

  2. Вызовите функцию signInWithPhoneNumber, передав ей номер телефона пользователя и созданный ранее объект RecaptchaVerifier.

    Web

    import { getAuth, signInWithPhoneNumber } from "firebase/auth";
    
    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    
    const auth = getAuth();
    signInWithPhoneNumber(auth, phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });

    Web

    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });
    Если при выполнении функции signInWithPhoneNumber возникает ошибка, сбросьте reCAPTCHA, чтобы пользователь мог повторить попытку:
    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    window.recaptchaVerifier.render().then(function(widgetId) {
      grecaptcha.reset(widgetId);
    });

Метод signInWithPhoneNumber показывает пользователю проверку reCAPTCHA, и если он ее проходит, то запрашивает у Firebase Authentication отправку SMS с кодом подтверждения на телефон пользователя.

Как войти в аккаунт с помощью кода подтверждения

После успешного вызова функции signInWithPhoneNumber предложите пользователю ввести код подтверждения, полученный в SMS. Затем войдите в аккаунт пользователя, передав код методу confirm объекта ConfirmationResult, который был передан обработчику выполнения signInWithPhoneNumber (то есть его блоку then). Пример:

Web

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

Web

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

Если вызов confirm выполнен успешно, пользователь войдет в аккаунт.

Получение промежуточного объекта AuthCredential

Если вам нужно получить объект AuthCredential для аккаунта пользователя, передайте код подтверждения из результата подтверждения и код подтверждения в PhoneAuthProvider.credential вместо вызова confirm:

var credential = firebase.auth.PhoneAuthProvider.credential(confirmationResult.verificationId, code);

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

firebase.auth().signInWithCredential(credential);

Тестирование с вымышленными номерами телефонов

Вы можете настроить вымышленные номера телефонов для разработки, используя консоль Firebase. Тестирование с вымышленными номерами телефонов дает следующие преимущества:

  • Проверьте аутентификацию по номеру телефона, не расходуя квоту на использование.
  • Проверять аутентификацию по номеру телефона без отправки SMS.
  • Проводить последовательные тесты с одним и тем же номером телефона без ограничений. Это снижает риск отклонения приложения при проверке в магазине, если проверяющий использует тот же номер телефона для тестирования.
  • Простое тестирование в средах разработки без дополнительных усилий, например возможность разрабатывать в симуляторе iOS или эмуляторе Android без сервисов Google Play.
  • Писать интеграционные тесты, не сталкиваясь с блокировками из-за проверок безопасности, которые обычно применяются к реальным номерам телефонов в рабочей среде.

Вымышленные номера телефонов должны соответствовать следующим требованиям:

  1. Убедитесь, что используемые номера телефонов вымышленные и не существуют в реальности. Firebase Authentication не позволяет использовать в качестве тестовых номеров существующие номера телефонов, которыми пользуются реальные пользователи. Один из вариантов – использовать в качестве тестовых номеров телефонов США номера с префиксом 555, например: +1 650-555-3434
  2. Номера телефонов должны иметь правильный формат, в том числе соответствовать требованиям к длине. Они проходят ту же проверку, что и номера реальных пользователей.
  3. Вы можете добавить до 10 номеров телефонов для разработки.
  4. Используйте сложные для подбора тестовые номера телефонов и коды и часто меняйте их.

Создание вымышленных номеров телефонов и кодов подтверждения

  1. В консоли Firebase выберите Безопасность > Аутентификация.
  2. На вкладке Способ входа включите поставщика услуг входа Телефон, если вы ещё этого не сделали.
  3. Разверните раздел Номера телефонов для тестирования.
  4. Укажите номер телефона, который хотите проверить, например:+1 650-555-3434.
  5. Введите шестизначный код подтверждения для этого номера, например: 654321.
  6. Нажмите Добавить для каждого номера. При необходимости вы можете удалить номер телефона и его код, наведя указатель на нужную строку и нажав на значок корзины.

Ручное тестирование

Вы можете сразу начать использовать вымышленный номер телефона в своем приложении. Это позволяет выполнять ручное тестирование на этапах разработки без проблем с квотами или регулированием скорости. Вы также можете проводить тестирование непосредственно в симуляторе iOS или эмуляторе Android без установленных сервисов Google Play.

Когда вы указываете вымышленный номер телефона и отправляете код подтверждения, SMS не отправляется. Вместо этого вам нужно ввести ранее настроенный код подтверждения.

После входа создается пользователь Firebase с указанным номером телефона. У пользователя с виртуальным номером телефона те же свойства и поведение, что и у пользователя с реальным номером телефона. Он может использовать Realtime Database/Cloud Firestore и другие сервисы так же, как и другие пользователи. Токен идентификатора, созданный в ходе этого процесса, имеет ту же подпись, что и у реального пользователя с номером телефона.

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

Тестирование интеграции

Помимо ручного тестирования, Firebase Authentication предоставляет API, которые помогают писать интеграционные тесты для проверки аутентификации по номеру телефона. Эти API отключают проверку приложений, отменяя требование reCAPTCHA в веб-версии и беззвучные push-уведомления в iOS. Это позволяет автоматизировать тестирование в этих процессах и упрощает его реализацию. Кроме того, они позволяют тестировать мгновенную проверку на устройствах Android.

В веб-версии задайте для параметра appVerificationDisabledForTesting значение true перед отрисовкой элемента firebase.auth.RecaptchaVerifier. Это автоматически решит reCAPTCHA, и вы сможете указать номер телефона, не проходя проверку вручную. Обратите внимание, что даже если reCAPTCHA отключена, войти в аккаунт не получится, если вы укажете недействительный номер телефона. С этим API можно использовать только вымышленные номера телефонов.

// Turn off phone auth app verification.
firebase.auth().settings.appVerificationDisabledForTesting = true;

var phoneNumber = "+16505554567";
var testVerificationCode = "123456";

// This will render a fake reCAPTCHA as appVerificationDisabledForTesting is true.
// This will resolve after rendering without app verification.
var appVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
// signInWithPhoneNumber will call appVerifier.verify() which will resolve with a fake
// reCAPTCHA response.
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
    .then(function (confirmationResult) {
      // confirmationResult can resolve with the fictional testVerificationCode above.
      return confirmationResult.confirm(testVerificationCode)
    }).catch(function (error) {
      // Error; SMS not sent
      // ...
    });

Видимые и невидимые поддельные средства проверки приложений reCAPTCHA ведут себя по-разному, когда проверка приложений отключена:

  • Видимая reCAPTCHA. Если видимая reCAPTCHA отрисовывается с помощью тега appVerifier.render(), она автоматически проходит проверку через несколько секунд. Это равносильно тому, что пользователь нажимает на reCAPTCHA сразу после ее загрузки. Ответ reCAPTCHA будет действителен в течение некоторого времени, а затем проблема будет устранена автоматически.
  • Невидимая проверка reCAPTCHA: Невидимая проверка reCAPTCHA не разрешается автоматически при отрисовке, а делает это при appVerifier.verify()вызове или при нажатии на кнопку reCAPTCHA после небольшой задержки. Аналогично, ответ будет действителен в течение определенного времени и автоматически разрешится только после appVerifier.verify() вызова или повторного нажатия на кнопку reCAPTCHA.

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

Дальнейшие действия

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

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

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

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

Чтобы выйти из аккаунта пользователя, вызовите функцию signOut:

Web

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

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

Web

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