Вы можете разрешить пользователям выполнять аутентификацию в Firebase с помощью Apple ID, используя Firebase SDK для реализации сквозного процесса входа OAuth 2.0.
Подготовка
Чтобы пользователи могли входить в систему с помощью Apple, сначала настройте функцию "Войти с Apple" на сайте для разработчиков Apple, а затем включите Apple в качестве поставщика учетных данных для входа в проект Firebase.
Как присоединиться к программе Apple Developer Program
Настроить функцию "Войти через аккаунт Apple" могут только участники программы для разработчиков Apple.
Как настроить вход через аккаунт Apple
В проекте Firebase должна быть включена и правильно настроена функция "Войти с Apple". На платформах Android и Apple конфигурация различается. Прежде чем продолжить, следуйте инструкциям из раздела "Настройка входа с Apple ID" руководств по платформам Apple и/или Android.Как включить Apple в качестве поставщика услуг входа
- В консоли Firebase выберите Безопасность > Аутентификация.
- На вкладке Способ входа включите поставщика для входа через Apple.
- Настройте параметры поставщика "Войти с Apple":
- Apple. Если вы развертываете приложение только на платформах Apple, поля "Идентификатор сервиса", "Идентификатор команды Apple", "Закрытый ключ" и "Идентификатор ключа" можно оставить пустыми.
-
Android. Чтобы поддерживать устройства Android, выполните следующие действия:
- Добавьте Firebase в проект для Android.
-
Укажите цифровой отпечаток SHA-1 приложения, если вы ещё этого не сделали.
-
В консоли Firebase перейдите на вкладку
Настройки > Общие. - Прокрутите страницу вниз до карточки Ваши приложения, выберите приложение Android и добавьте цифровой отпечаток SHA-1 в поле Цифровые отпечатки сертификата SHA.
О том, как получить цифровой отпечаток SHA приложения, можно прочитать в разделе об аутентификации клиентов.
-
В консоли Firebase перейдите на вкладку
-
Настройте параметры поставщика "Войти с Apple":
- В консоли Firebase выберите Безопасность > Аутентификация.
- На вкладке Способ входа нажмите на поставщика услуг входа Apple.
- Укажите идентификатор сервиса, созданный в предыдущем разделе. Кроме того, в разделе конфигурации потока кода OAuth укажите идентификатор команды Apple, закрытый ключ и идентификатор ключа, созданные в предыдущем разделе.
Соблюдайте требования Apple к анонимизированным данным
При входе через Apple ID пользователи могут анонимизировать свои данные, в том числе адрес электронной почты. Пользователи, которые выберут этот вариант, получат адреса электронной почты с доменом privaterelay.appleid.com. При использовании функции "Войти с Apple" в приложении вы должны соблюдать все применимые правила для разработчиков и условия использования Apple, касающиеся анонимизированных идентификаторов Apple.
В частности, перед тем как связать с анонимизированным идентификатором Apple ID любую информацию, позволяющую установить личность, вы должны получить согласие пользователя, если это необходимо. При использовании Firebase Authentication могут выполняться следующие действия:
- Связать адрес электронной почты с анонимным Apple ID или наоборот.
- Как связать номер телефона с анонимизированным Apple ID или наоборот
- Связать анонимный идентификатор Apple ID с неанонимными учетными данными социальной сети (Facebook, Google и т. д.) или наоборот.
Этот список не является исчерпывающим. Чтобы убедиться, что ваше приложение соответствует требованиям Apple, ознакомьтесь с лицензионным соглашением программы для разработчиков Apple в разделе "Членство" вашего аккаунта разработчика.
Как получить доступ к курсу firebase::auth::Auth
Класс Auth – это шлюз для всех вызовов API.
- Добавьте файлы заголовков Auth и App:
#include <cassert> #include <cstdint> #include "firebase/app.h" #include "firebase/auth.h"
- В коде инициализации создайте класс
firebase::App.#if defined(__ANDROID__) firebase::App* app = firebase::App::Create(firebase::AppOptions(), my_jni_env, my_activity); #else firebase::App* app = firebase::App::Create(firebase::AppOptions()); #endif // defined(__ANDROID__)
- Получите класс
firebase::auth::Authдля вашегоfirebase::App. МеждуAppиAuthсуществует однозначное соответствие.firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);
Как обрабатывать процесс входа с помощью Firebase SDK
Процесс входа с аккаунтом Apple различается на платформах Apple и Android.
На платформах Apple
Выполните аутентификацию пользователей с помощью Firebase через Apple Sign In Objective-C SDK, вызванный из кода C++.
Для каждого запроса на вход генерируйте случайную строку – "однократно используемый номер" (nonce), – которая позволит убедиться, что полученный идентификатор был предоставлен в ответ на запрос аутентификации вашего приложения. Этот шаг важен для предотвращения атак с повторным воспроизведением.
- (NSString *)randomNonce:(NSInteger)length { NSAssert(length > 0, @"Expected nonce to have positive length"); NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._"; NSMutableString *result = [NSMutableString string]; NSInteger remainingLength = length; while (remainingLength > 0) { NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16]; for (NSInteger i = 0; i < 16; i++) { uint8_t random = 0; int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random); NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode); [randoms addObject:@(random)]; } for (NSNumber *random in randoms) { if (remainingLength == 0) { break; } if (random.unsignedIntValue < characterSet.length) { unichar character = [characterSet characterAtIndex:random.unsignedIntValue]; [result appendFormat:@"%C", character]; remainingLength--; } } } }Вы отправите хеш SHA256 одноразового кода с запросом на вход, и Apple передаст его без изменений в ответе. Firebase проверяет ответ, хешируя исходное одноразовое число и сравнивая его со значением, переданным Apple.
Запустите процесс входа через Apple, включив в запрос хеш SHA256 для nonce и класс делегата, который будет обрабатывать ответ Apple (см. следующий шаг):
- (void)startSignInWithAppleFlow { NSString *nonce = [self randomNonce:32]; self.currentNonce = nonce; ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init]; ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest]; request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail]; request.nonce = [self stringBySha256HashingString:nonce]; ASAuthorizationController *authorizationController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]]; authorizationController.delegate = self; authorizationController.presentationContextProvider = self; [authorizationController performRequests]; } - (NSString *)stringBySha256HashingString:(NSString *)input { const char *string = [input UTF8String]; unsigned char result[CC_SHA256_DIGEST_LENGTH]; CC_SHA256(string, (CC_LONG)strlen(string), result); NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { [hashed appendFormat:@"%02x", result[i]]; } return hashed; }Обработайте ответ Apple в реализации ASAuthorizationControllerDelegate. Если вход выполнен успешно, используйте токен идентификатора из ответа Apple с хешированным одноразовым кодом для аутентификации в Firebase:
- (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) { if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) { ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential; NSString *rawNonce = self.currentNonce; NSAssert(rawNonce != nil, @"Invalid state: A login callback was received, but no login request was sent."); if (appleIDCredential.identityToken == nil) { NSLog(@"Unable to fetch identity token."); return; } NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken encoding:NSUTF8StringEncoding]; if (idToken == nil) { NSLog(@"Unable to serialize id token from data: %@", appleIDCredential.identityToken); } }Используйте полученную строку токена и исходное одноразовое число, чтобы создать Firebase Credential и войти в Firebase.
firebase::auth::OAuthProvider::GetCredential( /*provider_id=*/"apple.com", token, nonce, /*access_token=*/nullptr); firebase::Future<firebase::auth::AuthResult> result = auth->SignInAndRetrieveDataWithCredential(credential);Аналогичный шаблон можно использовать с функцией
Reauthenticate, которая позволяет получать новые учетные данные для выполнения конфиденциальных операций, требующих недавнего входа в систему.firebase::Future<firebase::auth::AuthResult> result = user->Reauthenticate(credential);По такому же принципу можно связать аккаунт с функцией "Войти с Apple". Однако при попытке связать аккаунт Apple с аккаунтом Firebase, который уже связан с другим аккаунтом Apple, может возникнуть ошибка. В этом случае в будущем будет возвращен статус
kAuthErrorCredentialAlreadyInUse, а вAuthResultможет содержаться действительныйcredential. Эти учетные данные можно использовать для входа в аккаунт, связанный с Apple, черезSignInAndRetrieveDataWithCredentialбез необходимости генерировать ещё один токен и одноразовый код для входа через Apple.firebase::Future<firebase::auth::AuthResult> link_result = auth->current_user().LinkWithCredential(credential); // To keep example simple, wait on the current thread until call completes. while (link_result.status() == firebase::kFutureStatusPending) { Wait(100); } // Determine the result of the link attempt if (link_result.error() == firebase::auth::kAuthErrorNone) { // user linked correctly. } else if (link_result.error() == firebase::auth::kAuthErrorCredentialAlreadyInUse && link_result.result() ->additional_user_info.updated_credential.is_valid()) { // Sign In with the new credential firebase::Future<firebase::auth::AuthResult> result = auth->SignInAndRetrieveDataWithCredential( link_result.result()->additional_user_info.updated_credential); } else { // Another link error occurred. }
На устройстве Android
На устройствах Android аутентификация пользователей выполняется с помощью Firebase. Для этого в приложение интегрируется универсальный вход OAuth на основе веб-технологий с использованием Firebase SDK.
Чтобы реализовать процесс входа с помощью Firebase SDK, выполните следующие действия:
Создайте экземпляр
FederatedOAuthProviderData, настроенный с идентификатором поставщика, подходящим для Apple.firebase::auth::FederatedOAuthProviderData provider_data("apple.com");Необязательно. Укажите дополнительные области действия OAuth 2.0, которые вы хотите запросить у поставщика услуг аутентификации.
provider_data.scopes.push_back("email"); provider_data.scopes.push_back("name");Необязательно. Если вы хотите, чтобы экран входа в аккаунт Apple отображался не на английском языке, задайте параметр
locale. Список поддерживаемых языков можно найти в документации по входу через Apple.// Localize to French. provider_data.custom_parameters["language"] = "fr"; ```После того как вы настроите данные поставщика, используйте их, чтобы создать FederatedOAuthProvider.
// Construct a FederatedOAuthProvider for use in Auth methods. firebase::auth::FederatedOAuthProvider provider(provider_data);Выполните аутентификацию в Firebase с помощью объекта поставщика аутентификации. Обратите внимание, что в отличие от других операций FirebaseAuth эта операция перехватывает управление интерфейсом, показывая веб-представление, в котором пользователь может ввести свои учетные данные.
Чтобы начать процесс входа, вызовите
signInWithProvider:firebase::Future<firebase::auth::AuthResult> result = auth->SignInWithProvider(provider_data);Затем приложение может подождать или зарегистрировать обратный вызов в Future.
Аналогичный шаблон можно использовать с функцией
ReauthenticateWithProvider, которая позволяет получать новые учетные данные для выполнения конфиденциальных операций, требующих недавнего входа в систему.firebase::Future<firebase::auth::AuthResult> result = user.ReauthenticateWithProvider(provider_data);Затем приложение может подождать или зарегистрировать обратный вызов в объекте Future.
Кроме того, с помощью
LinkWithCredential()можно связать разных поставщиков идентификационной информации с существующими аккаунтами.Обратите внимание, что Apple требует получать явное согласие пользователей перед тем, как связывать их аккаунты Apple с другими данными.
Например, чтобы связать аккаунт Facebook с текущим аккаунтом Firebase, используйте токен доступа, полученный при входе пользователя в Facebook:
// Initialize a Facebook credential with a Facebook access token. AuthCredential credential = firebase::auth::FacebookAuthProvider.getCredential(token); // Assuming the current user is an Apple user linking a Facebook provider. firebase::Future<firebase::auth::AuthResult> result = auth.current_user().LinkWithCredential(credential);
Как войти в аккаунт с помощью приложения "Заметки" от Apple
В отличие от других поставщиков, поддерживаемых Firebase Auth, Apple не предоставляет URL фотографии.
Если пользователь не хочет делиться своим адресом электронной почты с приложением, Apple предоставляет ему уникальный адрес (в формате xyz@privaterelay.appleid.com), который передается приложению. Если вы настроили службу ретрансляции частных электронных адресов, Apple пересылает письма, отправленные на анонимизированный адрес, на настоящий адрес пользователя.
Apple передает приложениям информацию о пользователе, например отображаемое имя, только при первом входе в аккаунт. Обычно Firebase сохраняет отображаемое имя при первом входе пользователя с помощью Apple ID. Получить его можно с помощью current_user().display_name(). Однако если вы ранее использовали Apple для входа пользователя в приложение без Firebase, Apple не предоставит Firebase отображаемое имя пользователя.
Дальнейшие действия
После первого входа пользователя создается новый аккаунт, связанный с учетными данными, которые он использовал при входе (именем пользователя и паролем, номером телефона или информацией поставщика услуг аутентификации). Этот новый аккаунт хранится в проекте Firebase и может использоваться для идентификации пользователя во всех приложениях проекта независимо от того, как он вошел в аккаунт.В своих приложениях вы можете получать основную информацию профиля пользователя из объекта firebase::auth::User. Подробнее о том, как управлять пользователями…
В правилах безопасности для базы данных Firebase Realtime Database и Cloud Storage можно получить уникальный идентификатор пользователя, выполнившего вход, из переменной auth и использовать его, чтобы контролировать, к каким данным у пользователя есть доступ.