Вы можете разрешить своим пользователям проходить аутентификацию в Firebase с использованием их Apple ID, используя Firebase SDK для реализации сквозного процесса входа OAuth 2.0.
Прежде чем начать
Чтобы выполнить вход пользователей с помощью Apple, сначала настройте функцию «Вход с Apple» на сайте разработчиков Apple, а затем включите Apple в качестве поставщика входа для вашего проекта Firebase.
Присоединяйтесь к программе разработчиков Apple
Функцию «Войти с Apple» могут настроить только участники программы разработчиков Apple .
Настроить вход с Apple
Вход через Apple должен быть включен и правильно настроен в вашем проекте Firebase. Конфигурация различается на платформах Android и Apple. Перед продолжением работы ознакомьтесь с разделом «Настройка входа через Apple» в руководствах по платформам Apple и/или Android .Включить Apple в качестве поставщика услуг входа
- В консоли Firebase откройте раздел «Аутентификация» . На вкладке «Метод входа» включите поставщика Apple .
- Настройте параметры поставщика входа Apple:
- Если вы развертываете свое приложение только на платформах Apple, вы можете оставить поля «Идентификатор сервиса», «Идентификатор команды Apple», «Закрытый ключ» и «Идентификатор ключа» пустыми.
- Для поддержки устройств Android:
- Добавьте Firebase в свой проект Android . Обязательно зарегистрируйте подпись SHA-1 вашего приложения при настройке в консоли Firebase .
- В консоли Firebase откройте раздел «Аутентификация» . На вкладке «Метод входа» включите поставщика Apple . Укажите идентификатор сервиса, созданный в предыдущем разделе. Кроме того, в разделе конфигурации потока кода OAuth укажите идентификатор Apple Team ID, а также закрытый ключ и идентификатор ключа, созданные в предыдущем разделе.
Соблюдайте требования Apple к анонимным данным
Функция «Войти с Apple» предоставляет пользователям возможность анонимизировать свои данные, включая адрес электронной почты, при входе в систему. Адреса электронной почты пользователей, выбравших эту опцию, находятся в домене privaterelay.appleid.com
. При использовании функции «Войти с Apple» в вашем приложении вы должны соблюдать все применимые политики разработчиков или условия Apple в отношении этих анонимизированных идентификаторов Apple ID.
Это включает получение любого необходимого согласия пользователя перед связыванием какой-либо персональной информации, позволяющей идентифицировать пользователя, с анонимизированным Apple ID. При использовании аутентификации Firebase это может включать следующие действия:
- Свяжите адрес электронной почты с анонимным Apple ID и наоборот.
- Привязать номер телефона к анонимному Apple ID и наоборот
- Свяжите неанонимные учетные данные социальной сети (Facebook, Google и т. д.) с анонимным Apple ID или наоборот.
Приведённый выше список не является исчерпывающим. Чтобы убедиться, что ваше приложение соответствует требованиям Apple, ознакомьтесь с лицензионным соглашением программы Apple Developer Program в разделе «Участие» вашей учётной записи разработчика.
Доступ к классу firebase::auth::Auth
Класс Auth
является шлюзом для всех вызовов API.- Добавьте заголовочные файлы Auth и App:
#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» — которую вы будете использовать, чтобы убедиться, что полученный вами токен ID был выдан именно в ответ на запрос аутентификации вашего приложения. Этот шаг важен для предотвращения атак повторного воспроизведения.
- (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 одноразового значения и класс делегата, который будет обрабатывать ответ 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 и входа в 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 Sign In. Однако вы можете столкнуться с ошибкой, если существующая учётная запись Firebase уже привязана к учётной записи Apple, которую вы пытаетесь привязать. В этом случае Future вернёт статус
kAuthErrorCredentialAlreadyInUse
, аAuthResult
может содержать действительныеcredential
. Эти учётные данные можно использовать для входа в учётную запись, привязанную к Apple, черезSignInAndRetrieveDataWithCredential
без необходимости генерации ещё одного токена и одноразового значения Apple Sign In.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 с помощью объекта провайдера Auth. Обратите внимание, что в отличие от других операций 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 Notes
В отличие от других поставщиков, поддерживаемых Firebase Auth, Apple не предоставляет URL-адрес фотографии.
Кроме того, когда пользователь решает не предоставлять приложению свой адрес электронной почты, Apple предоставляет ему уникальный адрес электронной почты (вида xyz@privaterelay.appleid.com
), который затем предоставляется вашему приложению. Если вы настроили службу ретрансляции приватной электронной почты, Apple пересылает письма, отправленные на анонимный адрес, на реальный адрес электронной почты пользователя.
Apple передает приложениям информацию о пользователе, такую как отображаемое имя, только при первом входе пользователя в систему. Обычно Firebase сохраняет отображаемое имя при первом входе пользователя в систему Apple. Его можно получить с помощью current_user().display_name()
. Однако, если вы ранее использовали Apple для входа пользователя в приложение без использования Firebase, Apple не предоставит Firebase отображаемое имя пользователя.
Следующие шаги
После первого входа пользователя в систему создаётся новая учётная запись, которая привязывается к учётным данным, использованным при входе (имя пользователя и пароль, номер телефона или информация о поставщике аутентификации). Эта новая учётная запись хранится в вашем проекте Firebase и может использоваться для идентификации пользователя в каждом приложении проекта, независимо от способа входа. В своих приложениях вы можете получить основную информацию о профиле пользователя из объекта firebase::auth::User
. См. раздел Управление пользователями .
В правилах безопасности Firebase Realtime Database и Cloud Storage вы можете получить уникальный идентификатор вошедшего в систему пользователя из переменной auth и использовать его для управления данными, к которым пользователь может получить доступ.