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ụ trò chơi của Google Play để đăng nhập cho người chơi vào một trò chơi Android được xây dựng trên Firebase và viết bằng C++. Để sử dụng tính năng đăng nhập bằng dịch vụ trò chơi của Google Play với Firebase, trước tiên, hãy đăng nhập cho người chơi bằng Google Play Games, rồi yêu cầu mã uỷ quyền OAuth 2.0 khi bạn làm như vậy. Sau đó, hãy truyền mã uỷ quyền đến PlayGamesAuthProvider để tạo thông tin xác thực Firebase mà bạn có thể dùng để xác thực bằng Firebase.

Trước khi bắt đầu

Để có thể sử dụng Firebase Authentication, bạn cần phải:

  • Đăng ký dự án C++ 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ì dự án đó đã được đăng ký và định cấu hình cho Firebase.

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

Xin lưu ý rằng việc thêm Firebase vào dự án C++ của bạn bao gồm các thao tác cả trong bảng điều khiển Firebase và trong dự án C++ đang mở (ví dụ: bạn tải 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. Chỉ định vân tay số SHA-1 của ứng dụng (nếu bạn chưa thực hiện).

    1. Trong bảng điều khiển Firebase, hãy chuyển đến thẻ Cài đặt > Chung.

    2. Di chuyển xuống thẻ Ứng dụng của bạn, chọn ứng dụng Android của bạn rồi thêm dấu vân tay SHA-1 vào trường Dấu vân tay chứng chỉ SHA.

    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

    Hãy xem phần Xác thực ứng dụng của bạn để biết thông tin chi tiết về cách lấy dấu vân tay SHA của ứng dụng.

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

    1. Trong bảng điều khiển Firebase, hãy chuyển đến phần Bảo mật > Xác thực.

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

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

      2. Sao chép mã ứng dụng khách và khoá bí mật của máy chủ web từ trình cung cấp dịch vụ đăng nhập bằng 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, đồng thời chỉ định mã ứng dụng khách và mật khẩu ứng dụng khách của máy chủ web trong dự án mà bạn đã nhận được ở bước cuối cùng.

Định cấu hình Play Games services bằng thông tin ứng dụng Firebase của bạn

  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 Play Games services > 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, rồi nhấp vào Sử dụng.

  4. Trên trang cấu hình Play Games services, hãy nhấp vào 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. Đảm bảo rằng đây là cùng một mã ứng dụng mà bạn đã chỉ định khi bật tính năng đăng nhập bằng Play Games.
    3. Lưu thay đổi.
  5. Vẫn ở trên trang cấu hình Play Games services, hãy nhấp lại vào 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 Android của dự án. (Nếu bạn không thấy mã ứng dụng Android, hãy nhớ đặt vân tay số SHA-1 của trò chơi trong bảng điều khiển Firebase.)
    3. Lưu thay đổi.
  6. Trên trang Người kiểm thử, hãy thêm địa chỉ email của những người dùng cần có khả năng đăng nhập vào trò chơi của bạn trước khi bạn phát hành trò chơi trên Play Store.

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

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

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

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

  1. Sao chép hoặc tải kho lưu trữ trình bổ trợ Đăng nhập bằng Google cho Unity 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 cách sử dụng Android Studio hoặc gradlew build.

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

  3. Đưa SDK Đăng nhập bằng Google C++ 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)

    ndk-build

    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 bản dựng SDK làm kho lưu trữ cục bộ:

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

    Và 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.5.1'
        // 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, hãy định cấu hình một đối tượng GoogleSignIn để sử dụng tính năng đăng nhập bằng Play Games và truy xuất mã uỷ quyền 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 Future do SignIn() trả về phân giải, 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ể dùng mã uỷ quyền để 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ã uỷ quyền cho tài khoản của người chơi.

  2. Sau đó, hãy trao đổi mã uỷ quyền từ dịch vụ trò chơi của Play để lấy thông tin đăng nhập Firebase và 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ó một 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 mỗi giây), bạn có thể kiểm tra kết quả một lần mỗi lầ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ể muốn đăng ký một lệnh gọi lại trên Future.

Đăng ký một 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 dựa trên sự kiện, bạn có thể muốn đăng ký các hàm gọi lại. Hàm callback được gọi khi Future hoàn tất.
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à 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, một tài khoản người dùng mới sẽ được tạo và liên kết với tên nhận dạng Play Games của họ. Tài khoản mới này được lưu trữ trong dự án Firebase của bạn và có thể dùng để xác định một người dùng trên mọi ứng dụng trong dự án của bạn.

Trong trò chơi, 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ơ sở dữ liệu theo thời gian thực của Firebase và Quy tắc bảo mật của Bộ nhớ đám mây, bạn có thể lấy mã nhận dạng người dùng duy nhất của người dùng đã đăng nhập từ biến auth và dùng mã nhận dạng này để kiểm soát dữ liệu mà người dùng có thể truy cập.

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

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

auth->SignOut();