Аутентификация с использованием Apple и C++

Вы можете разрешить своим пользователям проходить аутентификацию в 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 в качестве поставщика услуг входа

  1. В консоли Firebase откройте раздел «Аутентификация» . На вкладке «Метод входа» включите поставщика Apple .
  2. Настройте параметры поставщика входа Apple:
    1. Если вы развертываете свое приложение только на платформах Apple, вы можете оставить поля «Идентификатор сервиса», «Идентификатор команды Apple», «Закрытый ключ» и «Идентификатор ключа» пустыми.
    2. Для поддержки устройств Android:
      1. Добавьте Firebase в свой проект Android . Обязательно зарегистрируйте подпись SHA-1 вашего приложения при настройке в консоли Firebase .
      2. В консоли 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.
  1. Добавьте заголовочные файлы Auth и App:
    #include "firebase/app.h" #include "firebase/auth.h"
  2. В коде инициализации создайте класс 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__)
  3. Получите класс 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++.

  1. Для каждого запроса на вход генерируйте случайную строку — «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.

  2. Запустите процесс входа 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;   } 
  3. Обработайте ответ 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);       }     } 
  4. Используйте полученную строку токена и исходный одноразовый код для создания учетных данных Firebase и входа в Firebase.

    firebase::auth::OAuthProvider::GetCredential(         /*provider_id=*/"apple.com", token, nonce,         /*access_token=*/nullptr);  firebase::Future<firebase::auth::AuthResult> result =     auth->SignInAndRetrieveDataWithCredential(credential); 
  5. Тот же шаблон можно использовать с Reauthenticate , которая позволяет извлекать новые учетные данные для конфиденциальных операций, требующих недавнего входа в систему.

    firebase::Future<firebase::auth::AuthResult> result =     user->Reauthenticate(credential); 
  6. Тот же шаблон можно использовать для привязки учётной записи к 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, выполните следующие действия:

  1. Создайте экземпляр FederatedOAuthProviderData , настроенный с идентификатором провайдера, подходящим для Apple.

    firebase::auth::FederatedOAuthProviderData provider_data("apple.com"); 
  2. Необязательно: укажите дополнительные области OAuth 2.0, выходящие за рамки областей по умолчанию, которые вы хотите запросить у поставщика аутентификации.

    provider_data.scopes.push_back("email"); provider_data.scopes.push_back("name"); 
  3. Необязательно: если вы хотите, чтобы экран входа Apple отображался на языке, отличном от английского, задайте соответствующий locale стандарт. Поддерживаемые языковые стандарты см. в документации по входу с Apple .

    // Localize to French. provider_data.custom_parameters["language"] = "fr"; ``` 
  4. После настройки данных вашего провайдера используйте их для создания FederatedOAuthProvider.

    // Construct a FederatedOAuthProvider for use in Auth methods. firebase::auth::FederatedOAuthProvider provider(provider_data); 
  5. Выполните аутентификацию в Firebase с помощью объекта провайдера Auth. Обратите внимание, что в отличие от других операций FirebaseAuth, эта операция перехватит управление вашим пользовательским интерфейсом, открыв всплывающее веб-представление, в котором пользователь может ввести свои учётные данные.

    Чтобы начать процесс входа, вызовите signInWithProvider :

    firebase::Future<firebase::auth::AuthResult> result =   auth->SignInWithProvider(provider_data); 

    Затем ваше приложение может подождать или зарегистрировать обратный вызов в Future .

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

    firebase::Future<firebase::auth::AuthResult> result =   user.ReauthenticateWithProvider(provider_data); 

    Затем ваше приложение может подождать или зарегистрировать обратный вызов в Future .

  7. Кроме того, вы можете использовать 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 предоставляет ему уникальный адрес электронной почты (вида [email protected] ), который затем предоставляется вашему приложению. Если вы настроили службу ретрансляции приватной электронной почты, Apple пересылает письма, отправленные на анонимный адрес, на реальный адрес электронной почты пользователя.

Apple передает приложениям информацию о пользователе, такую ​​как отображаемое имя, только при первом входе пользователя в систему. Обычно Firebase сохраняет отображаемое имя при первом входе пользователя в систему Apple. Его можно получить с помощью current_user().display_name() . Однако, если вы ранее использовали Apple для входа пользователя в приложение без использования Firebase, Apple не предоставит Firebase отображаемое имя пользователя.

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

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

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

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