Аутентификация с помощью сервисов Google Play Games на C++

Вы можете использовать Google Play Games, чтобы игроки могли входить в игру для Android, созданную на основе Firebase и написанную на C++. Чтобы использовать вход через Google Play Games с Firebase, сначала войдите в аккаунт игрока с помощью Google Play Games и запросите код авторизации OAuth 2.0. Затем передайте код авторизации в PlayGamesAuthProvider, чтобы создать учетные данные Firebase, которые можно использовать для аутентификации в Firebase.

Подготовка

Чтобы использовать Firebase Authentication, вам необходимо:

  • Зарегистрируйте проект C++ и настройте его для использования Firebase.

    Если в проекте уже используется Firebase, можете пропустить этот шаг.

  • Добавьте Firebase C++ SDK в свой проект C++.

Обратите внимание, что для добавления Firebase в проект C++ нужно выполнить действия как в консоли Firebase, так и в вашем открытом проекте C++. Например, вам потребуется скачать файлы конфигурации Firebase из консоли, а затем переместить их в проект C++.

Как настроить проект Firebase

  1. Укажите цифровой отпечаток SHA-1 приложения, если вы ещё этого не сделали.

    1. В консоли Firebase перейдите на вкладку Настройки > Общие.

    2. Прокрутите страницу вниз до карточки Ваши приложения, выберите приложение Android и добавьте цифровой отпечаток SHA-1 в поле Цифровые отпечатки сертификата SHA.

    Вы можете получить хеш SHA сертификата подписи с помощью команды gradle signingReport:

    ./gradlew signingReport

    О том, как получить цифровой отпечаток SHA приложения, можно прочитать в разделе об аутентификации клиентов.

  2. Включите Google Play Games в качестве поставщика услуг входа:

    1. В консоли Firebase выберите Безопасность > Аутентификация.

    2. Создайте и получите идентификатор клиента веб-сервера и секрет клиента для своего проекта:

      1. На вкладке Способ входа включите поставщика услуг входа Google.

      2. Скопируйте идентификатор и секретный код клиента веб-сервера у поставщика услуг входа через Google.

    3. На вкладке Метод входа включите поставщика услуг входа Play Games и укажите идентификатор клиента и секретный код клиента веб-сервера проекта, полученные на предыдущем шаге.

Настройте Play Games services, используя информацию о приложении Firebase

  1. В консоли Google Play откройте приложение Google Play или создайте его.

  2. В разделе Развитие нажмите Play Games services > Настройка и управление > Конфигурация.

  3. Нажмите Да, в моей игре уже используются API Google, выберите проект Firebase из списка и нажмите Использовать.

  4. На странице конфигурации Play Games services нажмите Добавить учетные данные.

    1. Выберите тип Игровой сервер.
    2. В поле Клиент OAuth выберите идентификатор веб-клиента проекта. Убедитесь, что это тот же идентификатор клиента, который вы указали при включении входа через Play Games.
    3. Сохраните изменения.
  5. На странице конфигурации Play Games services снова нажмите Добавить учетные данные.

    1. Выберите тип Android.
    2. В поле Клиент OAuth выберите идентификатор клиента Android для своего проекта. Если вы не видите идентификатор клиента Android, убедитесь, что отпечаток SHA-1 игры задан в консоли Firebase.
    3. Сохраните изменения.
  6. На странице Тестировщики добавьте адреса электронной почты пользователей, которым нужно предоставить доступ к игре до ее выпуска в Play Store.

Как добавить в игру вход через Play Игры

Чтобы пользователи могли входить в игру, необходимо интегрировать вход через Google Play Игры.

Самый простой и рекомендуемый способ добавить в проект Android на C++ поддержку входа через Google Play Игры – использовать Google Sign-in C++ SDK.

Чтобы добавить в игру вход через аккаунт Play Games с помощью C++ SDK для входа с аккаунтом Google, выполните следующие действия:

  1. Клонируйте или скачайте репозиторий плагина Google Sign-In для Unity, в котором также есть C++ SDK.

  2. Соберите проект, содержащийся в каталоге staging/native/, с помощью Android Studio или gradlew build.

    Сборка копирует выходные данные в каталог с названием google-signin-cpp.

  3. Добавьте Google Sign-in C++ SDK в файл сборки нативного кода игры:

    CMake

    В файле CMakeLists.txt верхнего уровня:

    set(GSI_PACKAGE_DIR "/path/to/google-signin-cpp")
    add_library(lib-google-signin-cpp STATIC IMPORTED) set_target_properties(lib-google-signin-cpp PROPERTIES IMPORTED_LOCATION     ${GSI_PACKAGE_DIR}/lib/${ANDROID_ABI}/libgoogle-signin-cpp.a )
    ...
    target_link_libraries(     ...     lib-google-signin-cpp)

    ndk-build

    В файле Android.mk:

    include $(CLEAR_VARS)
    LOCAL_MODULE := google-signin-cpp
    GSI_SDK_DIR := /path/to/google-signin-cpp
    LOCAL_SRC_FILES := $(GSI_SDK_DIR)/lib/$(TARGET_ARCH_ABI)/libgoogle-signin-cpp.a
    LOCAL_EXPORT_C_INCLUDES := $(GSI_SDK_DIR)/include
    include $(PREBUILT_STATIC_LIBRARY)

  4. Затем добавьте вспомогательный компонент Java, который требуется для C++ SDK.

    Для этого в файле build.gradle на уровне проекта добавьте каталог выходных данных сборки SDK в качестве локального репозитория:

    allprojects {
        repositories {
            // ...
            flatDir {
                dirs 'path/to/google-signin-cpp'
            }
        }
    }
    

    В файле build.gradle на уровне модуля объявите вспомогательный компонент как зависимость:

    dependencies {
        implementation 'com.google.android.gms:play-services-auth:22.0.0'
        // Depend on the AAR built with the Google Sign-in SDK in order to add
        // the Java helper classes, which are used by the C++ library.
        compile(name:'google-signin-cpp-release', ext:'aar')
    }
    
  5. Затем в игре настройте объект GoogleSignIn, чтобы использовать вход через Play Games и получать код авторизации сервера:

    #include "google_signin.h"
    #include "future.h"
    
    using namespace google::signin;
    
    // ...
    
    GoogleSignIn::Configuration config = {};
    config.web_client_id = "YOUR_WEB_CLIENT_ID_HERE";
    config.request_id_token = false;
    config.use_game_signin = true;
    config.request_auth_code = true;
    
    GoogleSignIn gsi = GoogleSignIn(GetActivity(), GetJavaVM());
    gsi.Configure(config);
    
  6. Наконец, вызовите функцию SignIn(), чтобы войти в аккаунт Play Игр:

    Future<GoogleSignIn::SignInResult> &future = gsi.SignIn();
    

    Когда объект Future, возвращенный функцией SignIn(), будет разрешен, вы сможете получить код аутентификации сервера из результата:

    if (!future.Pending()) {
        const GoogleSignIn::StatusCode status =
                static_cast<GoogleSignIn::StatusCode>(future.Status());
        if (status == GoogleSignIn::kStatusCodeSuccess) {
            // Player successfully signed in to Google Play! Get auth code to
            //   pass to Firebase
            const GoogleSignIn::SignInResult result =
                    static_cast<GoogleSignIn::SignInResult>(future.Result());
            const char* server_auth_code = result.User.GetServerAuthCode();
        }
    }
    

Как пройти аутентификацию с помощью Firebase

После того как игрок войдет в аккаунт Play Игр, вы можете использовать код авторизации для аутентификации в Firebase.

  1. После того как игрок успешно войдет в аккаунт через Play Games, получите код авторизации для его аккаунта.

  2. Затем обменяйте код авторизации из сервисов Play Игр на учетные данные Firebase и используйте их для аутентификации игрока:

    firebase::auth::Credential credential =
        firebase::auth::PlayGamesAuthProvider::GetCredential(server_auth_code);
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  3. Если в вашей программе есть цикл обновления, который выполняется регулярно (например, 30 или 60 раз в секунду), вы можете проверять результаты один раз за обновление с помощью Auth::SignInAndRetrieveDataWithCredentialLastResult:

    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredentialLastResult();
    if (result.status() == firebase::kFutureStatusComplete) {
      if (result.error() == firebase::auth::kAuthErrorNone) {
        firebase::auth::AuthResult auth_result = *result.result();
        printf("Sign in succeeded for `%s`\n",
               auth_result.user.display_name().c_str());
      } else {
        printf("Sign in failed with error '%s'\n", result.error_message());
      }
    }

    Если ваша программа управляется событиями, вы можете зарегистрировать обратный вызов в Future.

Как зарегистрировать обратный вызов в Future

В некоторых программах есть Update функции, которые вызываются 30 или 60 раз в секунду. Например, многие игры работают по этой модели. Эти программы могут вызывать функции LastResult для опроса асинхронных вызовов. Однако если ваша программа управляется событиями, вам может быть удобнее зарегистрировать функции обратного вызова. Функция обратного вызова вызывается после завершения Future.
void OnCreateCallback(const firebase::Future<firebase::auth::User*>& result,
                      void* user_data) {
  // The callback is called when the Future enters the `complete` state.
  assert(result.status() == firebase::kFutureStatusComplete);

  // Use `user_data` to pass-in program context, if you like.
  MyProgramContext* program_context = static_cast<MyProgramContext*>(user_data);

  // Important to handle both success and failure situations.
  if (result.error() == firebase::auth::kAuthErrorNone) {
    firebase::auth::User* user = *result.result();
    printf("Create user succeeded for email %s\n", user->email().c_str());

    // Perform other actions on User, if you like.
    firebase::auth::User::UserProfile profile;
    profile.display_name = program_context->display_name;
    user->UpdateUserProfile(profile);

  } else {
    printf("Created user failed with error '%s'\n", result.error_message());
  }
}

void CreateUser(firebase::auth::Auth* auth) {
  // Callbacks work the same for any firebase::Future.
  firebase::Future<firebase::auth::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // `&my_program_context` is passed verbatim to OnCreateCallback().
  result.OnCompletion(OnCreateCallback, &my_program_context);
}
При желании функцию обратного вызова можно заменить лямбда-функцией.
void CreateUserUsingLambda(firebase::auth::Auth* auth) {
  // Callbacks work the same for any firebase::Future.
  firebase::Future<firebase::auth::AuthResult> result =
      auth->CreateUserWithEmailAndPasswordLastResult();

  // The lambda has the same signature as the callback function.
  result.OnCompletion(
      [](const firebase::Future<firebase::auth::User*>& result,
         void* user_data) {
        // `user_data` is the same as &my_program_context, below.
        // Note that we can't capture this value in the [] because std::function
        // is not supported by our minimum compiler spec (which is pre C++11).
        MyProgramContext* program_context =
            static_cast<MyProgramContext*>(user_data);

        // Process create user result...
        (void)program_context;
      },
      &my_program_context);
}

Дальнейшие действия

После первого входа пользователя создается новый аккаунт и связывается с его идентификатором Play Игр. Новый аккаунт хранится в проекте Firebase и позволяет идентифицировать пользователя во всех приложениях проекта.

В игре можно получить UID пользователя Firebase из объекта firebase::auth::User:

firebase::auth::User user = auth->current_user();
if (user.is_valid()) {
  std::string playerName = user.displayName();

  // The user's ID, unique to the Firebase project.
  // Do NOT use this value to authenticate with your backend server,
  // if you have one. Use firebase::auth::User::Token() instead.
  std::string uid = user.uid();
}

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

Чтобы получить информацию об игроке Play Games или доступ к сервисам Play Games, используйте API, предоставляемые C++ SDK сервисов Google Play Games.

Чтобы выйти из аккаунта пользователя, вызовите функцию SignOut():

auth->SignOut();