Fazer a autenticação usando os serviços relacionados a jogos do Google Play com C++

É possível usar os serviços relacionados a jogos do Google Play para fazer o login dos usuários em um jogo Android criado no Firebase e programado em C++. Para usar o login desses serviços com o Firebase, primeiro faça o login do jogador no Google Play Games e solicite um código de autenticação do OAuth 2.0. Em seguida, envie esse código para PlayGamesAuthProvider a fim de gerar uma credencial do Firebase que será usada para acessar a plataforma.

Antes de começar

Antes de usar o Firebase Authentication, você precisa:

  • Registrar seu projeto em C++ e configurá-lo para usar o Firebase

    Se o projeto em C++ já usa o Firebase, então ele já está registrado e configurado para o Firebase.

  • Adicione o SDK do Firebase para C++ ao seu projeto em C++.

Adicionar o Firebase ao projeto em C++ envolve tarefas no console do Firebase e no projeto em C++ aberto (por exemplo, fazer o download dos arquivos de configuração do Firebase pelo console e movê-los para o projeto em C++).

Configurar seu projeto do Firebase

  1. Caso você ainda não tenha configurado a impressão digital SHA-1 do jogo, faça isso na página de configurações do Console do Firebase.

    É possível retornar o hash SHA do certificado de assinatura com o comando signingReport do Gradle:

    ./gradlew signingReport

  2. Ative Google Play Games como um provedor de login:

    1. No console do Firebase, abra a seção Authentication.

    2. Gere e receba a chave secreta e o ID do ciente do servidor da Web do seu projeto:

      1. Na guia Método de login, ative o provedor de login do Google.

      2. Copie o ID do cliente do servidor da Web e o secret do provedor de login do Google.

    3. Na guia Método de login, ative o provedor de login Play Games e especifique o ID e a chave secreta do cliente do servidor da Web do seu projeto, que foi recebido na última etapa.

Configurar o Play Games services com as informações do app Firebase

  1. No Console do Google Play, abra o app Google Play ou crie um.

  2. Na seção Expandir, clique em Play Games services> Configuração e Gerenciamento > Configuração.

  3. Clique em Sim, meu jogo já usa APIs do Google, selecione seu projeto do Firebase na lista e clique em Usar.

  4. Na página de configuração do Play Games services, clique em Adicionar credencial.

    1. Selecione o tipo de Servidor de jogos.
    2. No campo Cliente OAuth, selecione o ID do cliente da Web do seu projeto. Verifique se esse é o mesmo ID do cliente especificado quando você ativou o login do Play Games.
    3. Salve as alterações.
  5. Ainda na página de configuração do Play Games services, clique em Adicionar credencial novamente.

    1. Selecione o tipo Android.
    2. No campo Cliente OAuth, selecione o ID do cliente Android do seu projeto. Caso você não veja o ID do cliente do Android, configure a impressão digital SHA-1 do jogo no Console do Firebase.
    3. Salve as alterações.
  6. Na página Testadores, adicione os endereços de e-mail de qualquer usuário que precise fazer login no seu jogo antes de liberá-lo na Play Store.

Integrar o login do Play Games ao seu jogo

Antes de permitir a conexão dos jogadores, é necessário integrar o login do Google Play Games ao seu jogo.

A maneira mais fácil e recomendável de adicionar a compatibilidade com o login do Play Games a um projeto do Android C++ é usar o SDK para C++ do Login do Google.

Para adicionar o login do Play Games ao seu jogo com o SDK para C++ do Login do Google, siga as seguintes etapas:

  1. Clone ou faça o download do repositório de plug-ins para Unity do Login do Google, que também contém o SDK para C ++.

  2. Crie o projeto no diretório staging/native/ usando o Android Studio ou gradlew build.

    A versão copia a saída do processo para um diretório chamado google-signin-cpp.

  3. Inclua o SDK para C++ do Login do Google no makefile do código nativo do seu jogo:

    CMake

    No seu arquivo CMakeLists.txt de nível superior:

    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

    No seu arquivo 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. Em seguida, inclua o componente de ajuda do Java exigido pelo SDK para C++.

    Para isso, adicione o diretório de saída da versão do SDK como um repositório local ao arquivo build.gradle no nível do seu projeto:

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

    Depois, declare o componente auxiliar como uma dependência no arquivo build.gradle no nível de módulo:

    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. Em seguida, configure no seu jogo um objeto GoogleSignIn para usar o login do Play Games e recuperar um código de autenticação do servidor:

    #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. Por fim, chame SignIn() para fazer login do jogador no Play Games:

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

    Quando a classe Future retornada por SignIn() for resolvida, você receberá o código de autenticação do servidor no resultado:

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

Autenticar com o Firebase

Depois que o jogador fizer o login com o Play Games, você poderá usar o código desse processo para fazer a autenticação com o Firebase.

  1. Depois que o usuário fizer login usando o Play Games, você receberá um código de autenticação para a conta do jogador.

  2. Em seguida, troque o código de autenticação dos serviços do Play Games por uma credencial do Firebase que será usada para autenticar o jogador:

    firebase::auth::Credential credential =
        firebase::auth::PlayGamesAuthProvider::GetCredential(server_auth_code);
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  3. Se o seu programa tiver uma rotina de atualização regular, por exemplo, 30 ou 60 vezes por segundo, será possível verificar os resultados uma vez por ciclo usando 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());
      }
    }

    Se o seu programa for voltado a eventos, vai ser uma boa ideia registrar um callback na classe Future.

Registrar um callback no Futuro

Alguns programas têm função Update que são chamadas 30 ou 60 vezes por segundo. Muitos jogos, por exemplo, seguem esse modelo. Esses programas podem invocar as funções LastResult para pesquisar chamadas assíncronas. Caso seu programa seja baseado em eventos, pode ser preferível registrar funções de retorno de chamada. Essas funções são chamadas na conclusão da classe 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);
}
Se você preferir, a função de retorno de chamada também pode ser um lambda.
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);
}

Próximas etapas

Depois que um usuário faz login pela primeira vez, uma nova conta de usuário é criada e vinculada ao ID do Play Games. Essa nova conta é armazenada como parte do seu projeto do Firebase e pode ser usada para identificar um usuário em cada app no seu projeto.

No seu jogo, é possível receber o UID do Firebase referente ao usuário usando o objeto 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();
}

Nas regras de segurança do Firebase Realtime Database e do Cloud Storage, é possível usar a variável auth para acessar o ID exclusivo do usuário que fez login. Use essa informação para controlar quais dados um usuário pode acessar.

Para ter acesso às informações de um jogador do Play Games ou para acessar os serviços relacionados a jogos do Google Play, use as APIs fornecidas pelo SDK desses serviços para C++.

Para desconectar um usuário, chame SignOut():

auth->SignOut();