C++ で Google Play ゲームサービスを使用して認証する

Google Play ゲームサービスを使用して、C++ で記述して Firebase で構築した Android ゲームにプレーヤーをログインさせることができます。Firebase で Google Play ゲームサービスのログインを使用するには、まず Google Play ゲームでプレーヤーをログインさせ、OAuth 2.0 の認証コードをリクエストします。次に、認証コードを PlayGamesAuthProvider に渡して Firebase 認証情報を生成します。この認証情報を Firebase での認証に使用できます。

準備

Android NDK プロジェクトに Firebase を追加する

Firebase C++ SDK 設定ガイドの Android の設定手順に沿って、Android NDK プロジェクトに Firebase を追加します。

Firebase C++ SDK をプロジェクトのネイティブ コードの make ファイルにインクルードします。

CMake

トップレベルの CMakeLists.txt ファイル内:

set(FIREBASE_CPP_SDK_DIR "/path/to/firebase_cpp_sdk")
string(REGEX REPLACE "(.*)_.*" "\\1" STL ${ANDROID_STL})

add_library(firebase_app STATIC IMPORTED)
set_target_properties(firebase_app PROPERTIES IMPORTED_LOCATION
    ${FIREBASE_CPP_SDK_DIR}/libs/android/${ANDROID_ABI}/${STL}/libapp.a)

add_library(firebase_auth STATIC IMPORTED)
set_target_properties(firebase_auth PROPERTIES IMPORTED_LOCATION
    ${FIREBASE_CPP_SDK_DIR}/libs/android/${ANDROID_ABI}/${STL}/libauth.a)

...

target_link_libraries(
    ...
    firebase_app
    firebase_auth)

ndk-build

Android.mk ファイル内:

STL := $(firstword $(subst _, ,$(APP_STL)))
FIREBASE_CPP_SDK_DIR := /path/to/frebase_cpp_sdk
FIREBASE_LIBRARY_PATH := \
    $(FIREBASE_CPP_SDK_DIR)/libs/android/$(TARGET_ARCH_ABI)/$(STL)

include $(CLEAR_VARS)
LOCAL_MODULE := firebase_app
LOCAL_SRC_FILES := $(FIREBASE_LIBRARY_PATH)/libapp.a
LOCAL_EXPORT_C_INCLUDES := $(FIREBASE_CPP_SDK_DIR)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := firebase_auth
LOCAL_SRC_FILES := $(FIREBASE_LIBRARY_PATH)/libauth.a
LOCAL_EXPORT_C_INCLUDES := $(FIREBASE_CPP_SDK_DIR)/include
include $(PREBUILT_STATIC_LIBRARY)

モジュール レベルの build.gradle ファイルで、Firebase を依存関係として宣言します。

dependencies {
    implementation 'com.google.firebase:firebase-auth:16.0.3'
    implementation 'com.google.android.gms:play-services-base:15.0.1'
    implementation 'com.google.android.gms:play-services-auth:16.0.0'
}

Firebase プロジェクトを設定する

  1. ゲームの SHA-1 フィンガープリントを、Firebase コンソールの [設定] ページで設定します。

    次のように keytool コマンドを使用して、キーの SHA-1 フィンガープリントを取得できます。

    keytool -exportcert -list -v \
        -alias YOUR-KEY-NAME -keystore PATH-TO-KEYSTORE

  2. 次のように Google Play ゲームをログイン プロバイダとして有効にします。

    1. プロジェクトのウェブサーバーのクライアント ID とクライアント シークレットを見つけます。ウェブサーバーのクライアント ID は、Google Play の認証サーバーでの Firebase プロジェクトの識別に使用されます。

      これらの値は次の手順で確認できます。

      1. Google API Console の認証情報ページで Firebase プロジェクトを開きます。
      2. [OAuth 2.0 クライアント ID] セクションで、ウェブ クライアント(Google サービスで自動作成)の詳細ページを開きます。このページに、ウェブサーバーのクライアント ID とクライアント シークレットが記載されています。
    2. 次に、Firebase コンソールで [Authentication] セクションを開きます。

    3. [ログイン方法] タブで、[Play ゲーム] ログイン プロバイダを有効にします。API Console で取得した、プロジェクトのウェブサーバーのクライアント ID とクライアント シークレットを指定する必要があります。

  1. Google Play Console を開き、[ゲーム サービス] をクリックします。
  2. [新しいゲームを追加] をクリックします。新しいゲームのダイアログで、[ゲームで Google API を既に使用しています] をクリックし、リスト内の Firebase プロジェクトの名前をクリックします。ゲームカテゴリを選択し、[次へ] をクリックして、[ゲームの詳細] ページに移動します。
  3. [ゲームの詳細] ページの最後で、必要な API がすべて有効になっていることを確認します。
  4. 次に、[リンク済みアプリ] ページを開き、[Android] をクリックします。ゲームのパッケージ名を指定し、[保存して次へ] をクリックします。コンソールに Android クライアント ID が表示されます。この値は無視してかまいません。
  5. [テスト] ページで、Play ストアにゲームをリリースする前に、ゲームにログインできるようにするユーザーのメールアドレスをホワイトリストに登録します。

Play ゲームのログインをゲームに統合する

プレーヤーをゲームにログインさせる前に、Google Play ゲームのログインを統合する必要があります。

C++ Android プロジェクトに Play ゲームのログインのサポートを追加する最も簡単な方法として、Google Sign-in C++ SDK を使用することが推奨されています。

Google Sign-in C++ SDK を使用して Play ゲームのログインをゲームに追加するには、次の操作を行います。

  1. Google Sign-in Unity plugin レポジトリをダウンロードするか、そのクローンを作成します。このレポジトリには C++ SDK も含まれています。

  2. staging/native/ ディレクトリに含まれるプロジェクトを、Android Studio または gradlew build を使用してビルドします。

    ビルドすると、その出力が google-signin-cpp という名前のディレクトリにコピーされます。

  3. ゲームのネイティブ コードの make ファイルに 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:16.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. 次に、ゲーム内で Play ゲームログインを使用してサーバー認証コードを取得できるように GoogleSignIn オブジェクトを設定します。

    #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();
    

    SignIn() によって返された Future が解決すると、結果からサーバー認証コードを取得できます。

    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 ゲームを使用して正常にログインした後、プレーヤーのアカウントの認証コードを取得します。

  2. その後、Play ゲームサービスの認証コードを Firebase 認証情報と交換し、Firebase 認証情報を使用してプレーヤーを認証します。

    firebase::auth::Credential credential =
        firebase::auth::PlayGamesAuthProvider::GetCredential(server_auth_code);
    firebase::Future<firebase::auth::User*> result =
        auth->SignInWithCredential(credential);
    
  3. 定期的に(たとえば、毎秒 30 回または 60 回)実行される更新ループがプログラムに含まれている場合、Auth::SignInWithCredentialLastResult を使用して、更新されるたびに 1 回結果を確認できます。

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

    プログラムがイベント駆動型の場合は、Future にコールバックを登録することをおすすめします。

Future にコールバックを登録する

プログラムの中には、毎秒 30 回または 60 回呼び出される Update 関数が含まれるものがあります。たとえば、多くのゲームでこのモデルが使用されています。このようなプログラムでは、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::User*> 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::User*> 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 ゲーム ID にリンクされます。この新しいアカウントは Firebase プロジェクトの一部として保存され、プロジェクト内のすべてのアプリでユーザーを特定するために使用できます。

ゲームでは、firebase::auth::User オブジェクトからユーザーの Firebase UID を取得できます。

firebase::auth::User* user = auth->current_user();
if (user != nullptr) {
  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 のセキュリティ ルールでは、ログイン済みユーザーの一意のユーザー ID を auth 変数から取得し、それを使用してどのデータをユーザーからアクセス可能にするかを制御できます。

ユーザーの Play ゲームのプレーヤー情報を取得したり、Play ゲームサービスにアクセスしたりするには、Google Play ゲームサービス C++ SDK で提供されている API を使用します。

ユーザーのログアウトを行うには SignOut() を呼び出します。

auth->SignOut();

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。