Xác thực bằng Dịch vụ trò chơi của Google Play với C++

Bạn có thể sử dụng dịch vụ Google Play Games để đăng nhập người chơi vào trò chơi Android được tạo trên Firebase và được viết bằng C++. Để sử dụng dịch vụ Google Play Games, hãy đăng nhập bằng Firebase, trước tiên hãy đăng nhập người chơi bằng Google Play Games và yêu cầu mã xác thực OAuth 2.0 khi thực hiện việc này. Sau đó, truyền mã xác thực vào PlayGamesAuthProvider để tạo thông tin đăng nhập Firebase. Bạn có thể sử dụng thông tin này để xác thực bằng Firebase.

Trước khi bắt đầu

Trước khi có thể sử dụng tính năng Xác thực Firebase, bạn cần:

  • Đăng ký dự án C++ của bạn và định cấu hình dự án đó để sử dụng Firebase.

    Nếu dự án C++ của bạn đã sử dụng Firebase, thì tức là dự án đó đã được đăng ký và định cấu hình cho Firebase.

  • Thêm SDK C++ của Firebase vào dự án C++.

Lưu ý rằng việc thêm Firebase vào dự án C++ sẽ liên quan đến các tác vụ trong cả bảng điều khiển của Firebase và trong dự án C++ đang mở (ví dụ: bạn tải các tệp cấu hình Firebase xuống từ bảng điều khiển, sau đó di chuyển các tệp đó vào dự án C++).

Thiết lập dự án Firebase

  1. Đặt vân tay số SHA-1 của trò chơi trên trang Cài đặt của bảng điều khiển của Firebase (nếu bạn chưa thiết lập).

    Bạn có thể lấy hàm băm SHA của chứng chỉ ký bằng lệnh signingReport của gradle:

    ./gradlew signingReport

  2. Dùng Google Play Games làm nhà cung cấp dịch vụ đăng nhập:

    1. Trong bảng điều khiển của Firebase, hãy mở mục Xác thực.

    2. Tạo và lấy mã ứng dụng khách của máy chủ web và mật khẩu ứng dụng khách của dự án:

      1. Trong thẻ Sign in method (Phương thức đăng nhập), hãy bật trình cung cấp dịch vụ đăng nhập của Google.

      2. Sao chép mã ứng dụng khách của máy chủ web và mã bí mật từ nhà cung cấp dịch vụ đăng nhập Google.

    3. Trong thẻ Phương thức đăng nhập, hãy bật trình cung cấp dịch vụ đăng nhập Play Games rồi chỉ định mã ứng dụng khách máy chủ web của dự án và mật khẩu ứng dụng khách mà bạn nhận được ở bước cuối cùng.

Định cấu hình Dịch vụ trò chơi của Play bằng thông tin ứng dụng trong Firebase

  1. Trong Google Play Console, hãy mở ứng dụng Google Play hoặc tạo một ứng dụng.

  2. Trong phần Phát triển, hãy nhấp vào Dịch vụ trò chơi của Play > Thiết lập và quản lý > Cấu hình.

  3. Nhấp vào Có, trò chơi của tôi đã sử dụng API của Google, chọn dự án Firebase của bạn trong danh sách, sau đó nhấp vào Sử dụng.

  4. Trên trang cấu hình Dịch vụ trò chơi của Play, hãy nhấp vào Add Credential (Thêm thông tin xác thực).

    1. Chọn loại Máy chủ trò chơi.
    2. Trong trường ứng dụng OAuth, hãy chọn mã ứng dụng web của dự án. Hãy đảm bảo đây chính là mã ứng dụng khách mà bạn đã chỉ định khi bật tính năng đăng nhập vào Play Games.
    3. Lưu thay đổi.
  5. Vẫn trên trang cấu hình Dịch vụ trò chơi của Play, hãy nhấp lại vào Add Credential (Thêm thông tin xác thực).

    1. Chọn loại Android.
    2. Trong trường ứng dụng OAuth, hãy chọn mã ứng dụng khách Android của dự án. (Nếu không thấy mã ứng dụng khách Android, hãy đảm bảo bạn đã đặt vân tay số SHA-1 của trò chơi trong bảng điều khiển của Firebase.)
    3. Lưu thay đổi.
  6. Trên trang Nhân viên kiểm thử, hãy thêm địa chỉ email của mọi người dùng cần có để có thể đăng nhập vào trò chơi trước khi bạn phát hành trò chơi trên Cửa hàng Play.

Tích hợp tính năng đăng nhập trên Play Games vào trò chơi của bạn

Trước khi có thể đăng nhập người chơi vào trò chơi của mình, bạn phải tích hợp tính năng đăng nhập vào Google Play Games.

Cách dễ nhất và nên dùng để thêm tính năng hỗ trợ đăng nhập vào Play Games vào dự án Android C++ là sử dụng SDK C++ đăng nhập bằng Google.

Để thêm tính năng đăng nhập vào Play Games vào trò chơi của bạn bằng SDK C++ dành cho đăng nhập bằng Google, hãy làm như sau:

  1. Sao chép hoặc tải Kho lưu trữ trình bổ trợ Unity đăng nhập bằng Google xuống, kho lưu trữ này cũng chứa SDK C++.

  2. Tạo dự án có trong thư mục staging/native/, bằng Android Studio hoặc gradlew build.

    Bản dựng sẽ sao chép dữ liệu đầu ra vào một thư mục có tên là google-signin-cpp.

  3. Đưa SDK C++ đăng nhập bằng Google vào tệp tạo mã gốc của trò chơi:

    CMake

    Trong tệp CMakeLists.txt cấp cao nhất:

    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)

    Bản dựng ndk

    Trong tệp 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. Tiếp theo, hãy thêm thành phần trợ giúp Java mà SDK C++ yêu cầu.

    Để thực hiện việc này, trong tệp build.gradle cấp dự án, hãy thêm thư mục đầu ra của bản dựng SDK làm kho lưu trữ cục bộ:

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

    Đồng thời, trong tệp build.gradle cấp mô-đun, hãy khai báo thành phần trợ giúp dưới dạng một phần phụ thuộc:

    dependencies {
        implementation 'com.google.android.gms:play-services-auth:21.2.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. Sau đó, trong trò chơi của bạn, hãy định cấu hình đối tượng GoogleSignIn để sử dụng tính năng đăng nhập vào Play Games và truy xuất mã xác thực máy chủ:

    #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. Cuối cùng, hãy gọi SignIn() để đăng nhập người chơi vào Play Games:

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

    Khi phân giải Tương lai do SignIn() trả về, bạn có thể lấy mã xác thực máy chủ từ kết quả:

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

Xác thực bằng Firebase

Sau khi người chơi đăng nhập bằng Play Games, bạn có thể sử dụng mã xác thực để xác thực bằng Firebase.

  1. Sau khi người chơi đăng nhập thành công bằng Play Games, hãy lấy mã xác thực cho tài khoản của người chơi.

  2. Sau đó, hãy trao đổi mã xác thực từ Dịch vụ trò chơi của Play để lấy thông tin xác thực Firebase và sử dụng thông tin đăng nhập Firebase để xác thực người chơi:

    firebase::auth::Credential credential =
        firebase::auth::PlayGamesAuthProvider::GetCredential(server_auth_code);
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  3. Nếu chương trình của bạn có vòng lặp cập nhật chạy thường xuyên (chẳng hạn như 30 hoặc 60 lần/giây), thì bạn có thể kiểm tra kết quả một lần cho mỗi bản cập nhật bằng 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());
      }
    }

    Hoặc nếu chương trình của bạn dựa trên sự kiện, bạn có thể ưu tiên đăng ký một lệnh gọi lại trong Future.

Đăng ký lệnh gọi lại trên Future

Một số chương trình có các hàm Update được gọi 30 hoặc 60 lần mỗi giây. Ví dụ: nhiều trò chơi tuân theo mô hình này. Các chương trình này có thể gọi các hàm LastResult để thăm dò các lệnh gọi không đồng bộ. Tuy nhiên, nếu chương trình của bạn hướng sự kiện, thì bạn nên đăng ký các hàm callback. Hàm callback được gọi sau khi hoàn thành 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);
}
Hàm callback cũng có thể là một lambda, nếu bạn muốn.
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);
}

Các bước tiếp theo

Sau khi người dùng đăng nhập lần đầu tiên, một tài khoản người dùng mới sẽ được tạo và liên kết với mã nhận dạng Play Games của họ. Tài khoản mới này được lưu trữ như một phần trong dự án Firebase của bạn và có thể dùng để xác định người dùng trên mọi ứng dụng trong dự án của bạn.

Trong trò chơi của mình, bạn có thể lấy UID Firebase của người dùng từ đối tượng 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();
}

Trong các Quy tắc bảo mật của Cloud Storage và Cơ sở dữ liệu theo thời gian thực của Firebase, bạn có thể lấy mã nhận dạng người dùng riêng biệt của người dùng đã đăng nhập từ biến auth và sử dụng mã đó để kiểm soát dữ liệu mà người dùng có thể truy cập.

Để lấy thông tin của người dùng về người chơi trên Play Games hoặc để truy cập vào dịch vụ trò chơi của Play, hãy sử dụng các API do SDK C++ trong dịch vụ Google Play Games cung cấp.

Để đăng xuất một người dùng, hãy gọi SignOut():

auth->SignOut();