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

Вы можете разрешить пользователям аутентифицироваться в Firebase, используя свой Apple ID, с помощью Firebase SDK, который обеспечивает сквозной процесс авторизации OAuth 2.0.

Прежде чем начать

Для авторизации пользователей с помощью Apple сначала настройте функцию «Вход с помощью Apple» на сайте разработчиков Apple, а затем включите Apple в качестве поставщика авторизации для вашего проекта Firebase.

Присоединяйтесь к программе для разработчиков Apple!

Настроить функцию «Вход с помощью Apple» могут только участники программы Apple Developer Program .

Настройка входа в систему с помощью Apple

В вашем проекте Firebase необходимо включить и правильно настроить вход через Apple. Настройки различаются для платформ Android и Apple. Перед продолжением ознакомьтесь с разделом «Настройка входа через Apple» в руководствах для платформ Apple и/или Android .

Включите Apple в качестве поставщика услуг авторизации.

  1. В консоли Firebase перейдите в раздел Безопасность > Аутентификация .
  2. На вкладке «Способ входа» включите поставщика услуг входа Apple .
  3. Настройте параметры поставщика авторизации Apple:
    • Apple : Если вы развертываете свое приложение только на платформах Apple, вы можете оставить поля «Идентификатор службы», «Идентификатор команды Apple», «Закрытый ключ» и «Идентификатор ключа» пустыми.
    • Android : Для поддержки устройств Android выполните следующие действия:
      1. Добавьте Firebase в свой Android-проект .
      2. Укажите SHA-1-отпечаток вашего приложения, если вы еще этого не сделали.
        1. В консоли Firebase перейдите в... > Вкладка «Общие» .
        2. Прокрутите вниз до карточки «Ваши приложения» , выберите свое приложение для Android и добавьте отпечаток SHA-1 в поле «Отпечатки сертификатов SHA» .

        Подробную информацию о том, как получить SHA-отпечаток вашего приложения, см. в разделе «Аутентификация клиента» .

      3. Настройте параметры поставщика авторизации Apple:
        1. В консоли Firebase перейдите в раздел Безопасность > Аутентификация .
        2. На вкладке «Способ входа» выберите поставщика услуг входа Apple .
        3. Укажите идентификатор службы, созданный вами в предыдущем разделе. Также в разделе конфигурации потока кода OAuth укажите свой идентификатор команды Apple, а также закрытый ключ и идентификатор ключа, созданные вами в предыдущем разделе.

Соблюдайте требования Apple к анонимизации данных.

Функция «Вход с Apple» предоставляет пользователям возможность анонимизировать свои данные, включая адрес электронной почты, при входе в систему. Пользователи, выбравшие этот вариант, имеют адреса электронной почты с доменом privaterelay.appleid.com . При использовании функции «Вход с Apple» в вашем приложении необходимо соблюдать все применимые правила и условия Apple для разработчиков, касающиеся этих анонимизированных Apple ID.

Это включает в себя получение необходимого согласия пользователя, прежде чем связывать какую-либо непосредственно идентифицирующую личную информацию с анонимизированным Apple ID. При использовании аутентификации Firebase это может включать следующие действия:

  • Привяжите адрес электронной почты к анонимизированному Apple ID или наоборот.
  • Привязать номер телефона к анонимизированному Apple ID или наоборот.
  • Привяжите неанонимные учетные данные из социальной сети (Facebook, Google и т. д.) к анонимизированному Apple ID или наоборот.

Приведенный выше список не является исчерпывающим. Чтобы убедиться, что ваше приложение соответствует требованиям Apple, ознакомьтесь с Лицензионным соглашением программы Apple для разработчиков в разделе «Участие» вашей учетной записи разработчика.

Получите доступ к классу 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, используя SDK Apple Sign In Objective-C, вызываемый из вашего кода C++.

  1. Для каждого запроса на вход в систему генерируйте случайную строку — «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 одноразового числа (nonce), который Apple передаст в ответе без изменений. Firebase проверяет ответ, хешируя исходное значение nonce и сравнивая его со значением, переданным Apple.

  2. Запустите процесс авторизации 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;
      }
    
  3. Обработайте ответ Apple в вашей реализации `ASAuthorizationControllerDelegate`. Если вход в систему прошел успешно, используйте токен ID из ответа Apple вместе с нехешированным nonce для аутентификации в 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. Используйте полученную строку токена и исходный nonce для создания учетных данных 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 без необходимости генерации нового токена и nonce для 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 с использованием SDK Firebase для обеспечения сквозного процесса авторизации.

Для обработки процесса авторизации с помощью 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 осуществляется с помощью объекта поставщика аутентификации. Обратите внимание, что в отличие от других операций 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 создает для этого пользователя уникальный адрес электронной почты (в формате 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 и использовать его для управления доступом пользователя к данным.