Если вы перешли на версию Firebase Authentication with Identity Platform, то можете добавить в приложение для iOS многофакторную аутентификацию по SMS.
Многофакторная аутентификация повышает безопасность приложения. Злоумышленники часто взламывают пароли и аккаунты в социальных сетях, но перехватить текстовое сообщение сложнее.
Подготовка
Включите хотя бы одного поставщика, поддерживающего многофакторную аутентификацию. Все поставщики поддерживают многофакторную аутентификацию, кроме аутентификации по телефону, анонимной аутентификации и Apple Game Center.
Убедитесь, что ваше приложение проверяет адреса электронной почты пользователей. Для многофакторной аутентификации требуется подтверждение адреса электронной почты. Это не позволит злоумышленникам зарегистрироваться в сервисе с чужим адресом электронной почты и заблокировать доступ к нему, добавив второй фактор.
Как включить многофакторную аутентификацию
В консоли Firebase выберите Безопасность > Аутентификация.
На вкладке Способ входа в разделе Расширенные настройки включите Многофакторную аутентификацию с помощью SMS.
Также укажите номера телефонов, которые вы будете использовать для тестирования приложения. Регистрация тестовых номеров телефонов не является обязательной, но настоятельно рекомендуется, чтобы избежать ограничения скорости передачи данных во время разработки.
Если вы ещё не авторизовали домен приложения, сделайте следующее:
В консоли Firebase выберите Безопасность > Аутентификация > вкладка Настройки.
В разделе Авторизованные домены нажмите Добавить домен и добавьте свой домен.
Проверка приложения
Authentication необходимо убедиться, что запросы SMS поступают из вашего приложения. Это можно сделать двумя способами:
Тихие уведомления APNs. Когда вы впервые выполняете вход пользователя, Authentication может отправить на устройство пользователя тихое push-уведомление. Если приложение получит уведомление, аутентификация будет выполнена. Обратите внимание, что начиная с iOS 8.0 для использования этого метода не нужно запрашивать у пользователя разрешение на отправку push-уведомлений.
Проверка reCAPTCHA. Если вы не можете отправить тихое уведомление (например, потому что пользователь отключил обновление контента в фоновом режиме или вы тестируете приложение в симуляторе iOS), можно использовать reCAPTCHA. Во многих случаях reCAPTCHA будет решаться автоматически без участия пользователя.
Как использовать беззвучные уведомления
Чтобы включить уведомления APNs для использования с Authentication:
В Xcode включите push-уведомления для своего проекта.
Загрузите ключ аутентификации APNs с помощью консоли Firebase (изменения будут автоматически перенесены в Google Cloud Authentication). Если у вас ещё нет ключа аутентификации APNs, узнайте, как его получить, из статьи Настройка APNs с помощью FCM.
В консоли Firebase выберите
Настройки > Общие. Затем нажмите на вкладку Обмен сообщениями в облаке.В разделе Ключ аутентификации APNs в настройках приложения для iOS нажмите Загрузить, чтобы загрузить ключ аутентификации для разработки, ключ аутентификации для производства или оба ключа. Обязательно заполните как минимум одно из этих полей.
Выберите ключ.
Добавьте идентификатор ключа. Идентификатор ключа можно найти в разделе Certificates, Identifiers & Profiles (Сертификаты, идентификаторы и профили) в Центре разработчиков Apple.
Нажмите Загрузить.
Если у вас уже есть сертификат APNs, вы можете загрузить его.
Как использовать проверку reCAPTCHA
Чтобы включить использование reCAPTCHA в клиентском SDK:
Откройте конфигурацию проекта в Xcode.
Дважды нажмите на название проекта в дереве слева.
В разделе Целевые значения выберите приложение.
Выберите вкладку Информация.
Разверните раздел Типы URL.
Нажмите кнопку +.
Введите обратный идентификатор клиента в поле Схемы URL. Это значение указано в файле конфигурации
GoogleService-Info.plistкакREVERSED_CLIENT_ID.
После этого конфигурация должна выглядеть примерно так:

При желании вы можете настроить внешний вид значков SFSafariViewController или UIWebView, которые показываются в приложении вместе с reCAPTCHA. Для этого создайте пользовательский класс, соответствующий протоколу FIRAuthUIDelegate, и передайте его в verifyPhoneNumber:UIDelegate:completion:.
Выбор способа регистрации
Вы можете выбрать, требуется ли для вашего приложения многофакторная аутентификация, а также как и когда регистрировать пользователей. Вот некоторые распространенные паттерны:
Зарегистрируйте второй фактор аутентификации пользователя в рамках регистрации. Используйте этот метод, если для вашего приложения требуется многофакторная аутентификация для всех пользователей. Обратите внимание, что для регистрации второго фактора в аккаунте должен быть подтвержден адрес электронной почты, поэтому в процессе регистрации необходимо предусмотреть этот шаг.
Предлагайте пользователям зарегистрировать второй фактор во время регистрации, но не делайте это обязательным. Приложения, в которых многофакторная аутентификация не обязательна, но рекомендуется, могут использовать этот подход.
Предоставьте возможность добавить второй фактор на странице управления аккаунтом или профилем пользователя, а не на экране регистрации. Это упрощает процесс регистрации, но при этом позволяет пользователям, которым важна безопасность, включить многофакторную аутентификацию.
Постепенно добавлять второй фактор, когда пользователь хочет получить доступ к функциям с повышенными требованиями к безопасности.
Как добавить второй этап
Чтобы зарегистрировать новый дополнительный фактор для пользователя:
Повторно аутентифицируйте пользователя.
Попросите пользователя ввести номер телефона.
убедитесь, что получили от них согласие.Получите сеанс с многофакторной аутентификацией для пользователя:
Swift
authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in // ... }Objective-C
[authResult.user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session, NSError * _Nullable error) { // ... }];Отправьте пользователю на телефон сообщение с кодом подтверждения. Убедитесь, что номер телефона начинается с
+и не содержит других знаков препинания или пробелов (например,+15105551234).Swift
// Send SMS verification code. PhoneAuthProvider.provider().verifyPhoneNumber( phoneNumber, uiDelegate: nil, multiFactorSession: session) { (verificationId, error) in // verificationId will be needed for enrollment completion. }Objective-C
// Send SMS verification code. [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber UIDelegate:nil multiFactorSession:session completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { // verificationId will be needed for enrollment completion. }];Хотя это и не обязательно, мы рекомендуем заранее сообщать пользователям, что им будет отправлено SMS-сообщение, и что за него взимается плата по стандартным тарифам.
Метод
verifyPhoneNumber()запускает проверку приложения в фоновом режиме с помощью скрытого push-уведомления. Если бесшумное push-уведомление недоступно,вместо него будет показана проверка reCAPTCHA.После отправки кода попросите пользователя подтвердить его. Затем используйте полученный ответ, чтобы создать
PhoneAuthCredential:Swift
// Ask user for the verification code. Then: let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationId, verificationCode: verificationCode)Objective-C
// Ask user for the SMS verification code. Then: FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID verificationCode:kPhoneSecondFactorVerificationCode];Инициализируйте объект утверждения:
Swift
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)Objective-C
FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];Завершите регистрацию. При необходимости можно указать отображаемое название для второго фактора. Это удобно для пользователей, у которых настроено несколько способов двухэтапной аутентификации, поскольку номер телефона маскируется во время аутентификации (например, +1******1234).
Swift
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in // ... }Objective-C
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. [authResult.user.multiFactor enrollWithAssertion:assertion displayName:nil completion:^(NSError * _Nullable error) { // ... }];
Ниже приведен полный пример кода для регистрации второго фактора:
Swift
let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
// Send SMS verification code.
PhoneAuthProvider.provider().verifyPhoneNumber(
phoneNumber,
uiDelegate: nil,
multiFactorSession: session
) { (verificationId, error) in
// verificationId will be needed for enrollment completion.
// Ask user for the verification code.
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationId!,
verificationCode: phoneSecondFactorVerificationCode)
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
// Complete enrollment. This will update the underlying tokens
// and trigger ID token change listener.
user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
// ...
}
}
})
Objective-C
FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
NSError * _Nullable error) {
// Send SMS verification code.
[FIRPhoneAuthProvider.provider
verifyPhoneNumber:phoneNumber
UIDelegate:nil
multiFactorSession:session
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
// verificationId will be needed for enrollment completion.
// Ask user for the verification code.
// ...
// Then:
FIRPhoneAuthCredential *credential =
[FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
verificationCode:kPhoneSecondFactorVerificationCode];
FIRMultiFactorAssertion *assertion =
[FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
// Complete enrollment. This will update the underlying tokens
// and trigger ID token change listener.
[user.multiFactor enrollWithAssertion:assertion
displayName:displayName
completion:^(NSError * _Nullable error) {
// ...
}];
}];
}];
Поздравляем! Вы успешно зарегистрировали второй фактор аутентификации для пользователя.
Вход пользователей с использованием второго фактора
Чтобы войти в аккаунт пользователя с помощью двухфакторной аутентификации по SMS:
Войдите в аккаунт пользователя с помощью первого фактора, а затем перехватите ошибку, указывающую на то, что требуется многофакторная аутентификация. Эта ошибка содержит средство устранения неполадок, подсказки о зарегистрированных дополнительных факторах и базовый сеанс, подтверждающий успешную аутентификацию пользователя с помощью первого фактора.
Например, если первым фактором был адрес электронной почты и пароль:
Swift
Auth.auth().signIn( withEmail: email, password: password ) { (result, error) in let authError = error as NSError if authError?.code == AuthErrorCode.secondFactorRequired.rawValue { // The user is a multi-factor user. Second factor challenge is required. let resolver = authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver // ... } else { // Handle other errors such as wrong password. } }Objective-C
[FIRAuth.auth signInWithEmail:email password:password completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) { // User is not enrolled with a second factor and is successfully signed in. // ... } else { // The user is a multi-factor user. Second factor challenge is required. } }];Если первым фактором пользователя является федеративный поставщик, например OAuth, перехватите ошибку после вызова
getCredentialWith().Если у пользователя настроено несколько дополнительных факторов, спросите, какой из них он хочет использовать. Замаскированный номер телефона можно получить с помощью
resolver.hints[selectedIndex].phoneNumber, а отображаемое имя – с помощьюresolver.hints[selectedIndex].displayName.Swift
// Ask user which second factor to use. Then: if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID { // User selected a phone second factor. // ... } else if resolver.hints[selectedIndex].factorID == TotpMultiFactorID { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }Objective-C
FIRMultiFactorResolver *resolver = (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; // Ask user which second factor to use. Then: FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex]; if (hint.factorID == FIRPhoneMultiFactorID) { // User selected a phone second factor. // ... } else if (hint.factorID == FIRTOTPMultiFactorID) { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }Отправьте пользователю сообщение с кодом подтверждения на телефон:
Swift
// Send SMS verification code. let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo PhoneAuthProvider.provider().verifyPhoneNumber( with: hint, uiDelegate: nil, multiFactorSession: resolver.session ) { (verificationId, error) in // verificationId will be needed for sign-in completion. }Objective-C
// Send SMS verification code [FIRPhoneAuthProvider.provider verifyPhoneNumberWithMultiFactorInfo:hint UIDelegate:nil multiFactorSession:resolver.session completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { if (error != nil) { // Failed to verify phone number. } }];После отправки SMS-кода попросите пользователя подтвердить его и использовать для создания
PhoneAuthCredential:Swift
// Ask user for the verification code. Then: let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationId!, verificationCode: verificationCodeFromUser)Objective-C
// Ask user for the SMS verification code. Then: FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID verificationCode:verificationCodeFromUser];Инициализируйте объект утверждения с помощью учетных данных:
Swift
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)Objective-C
FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];Войдите в аккаунт. После этого вы можете получить доступ к результату входа, который включает стандартные данные поставщика и учетные данные для аутентификации:
Swift
// Complete sign-in. This will also trigger the Auth state listeners. resolver.resolveSignIn(with: assertion) { (authResult, error) in // authResult will also contain the user, additionalUserInfo, optional // credential (null for email/password) associated with the first factor sign-in. // For example, if the user signed in with Google as a first factor, // authResult.additionalUserInfo will contain data related to Google provider that // the user signed in with. // user.credential contains the Google OAuth credential. // user.credential.accessToken contains the Google OAuth access token. // user.credential.idToken contains the Google OAuth ID token. }Objective-C
// Complete sign-in. [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error != nil) { // User successfully signed in with the second factor phone number. } }];
Ниже приведен пример кода для входа пользователя с многофакторной аутентификацией:
Swift
Auth.auth().signIn(
withEmail: email,
password: password
) { (result, error) in
let authError = error as NSError?
if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
let resolver =
authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
// Ask user which second factor to use.
// ...
// Then:
let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
// Send SMS verification code
PhoneAuthProvider.provider().verifyPhoneNumber(
with: hint,
uiDelegate: nil,
multiFactorSession: resolver.session
) { (verificationId, error) in
if error != nil {
// Failed to verify phone number.
}
// Ask user for the SMS verification code.
// ...
// Then:
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationId!,
verificationCode: verificationCodeFromUser)
let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
// Complete sign-in.
resolver.resolveSignIn(with: assertion) { (authResult, error) in
if error != nil {
// User successfully signed in with the second factor phone number.
}
}
}
}
}
Objective-C
[FIRAuth.auth signInWithEmail:email
password:password
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
// User is not enrolled with a second factor and is successfully signed in.
// ...
} else {
FIRMultiFactorResolver *resolver =
(FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
// Ask user which second factor to use.
// ...
// Then:
FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
// Send SMS verification code
[FIRPhoneAuthProvider.provider
verifyPhoneNumberWithMultiFactorInfo:hint
UIDelegate:nil
multiFactorSession:resolver.session
completion:^(NSString * _Nullable verificationID,
NSError * _Nullable error) {
if (error != nil) {
// Failed to verify phone number.
}
// Ask user for the SMS verification code.
// ...
// Then:
FIRPhoneAuthCredential *credential =
[FIRPhoneAuthProvider.provider
credentialWithVerificationID:verificationID
verificationCode:kPhoneSecondFactorVerificationCode];
FIRMultiFactorAssertion *assertion =
[FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
// Complete sign-in.
[resolver resolveSignInWithAssertion:assertion
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (error != nil) {
// User successfully signed in with the second factor phone number.
}
}];
}];
}
}];
Поздравляем! Пользователь успешно вошел в аккаунт с использованием многофакторной аутентификации.
Дальнейшие действия
- Управлять пользователями с многофакторной аутентификацией программно с помощью Admin SDK.