Acceso del servidor a los Servicios de juego de Google Play

Te recomendamos que uses PgsGamesSignInClient para autenticar a los jugadores y pasar su identidad al servidor de backend de forma segura. De esta manera, el juego puede recuperar de forma segura la identidad del jugador y otros datos sin exponerse a posibles manipulaciones mientras pasa por el dispositivo.

Una vez que el jugador se autentique correctamente, podrás solicitar un código especial de un solo uso (que se denomina código de autorización del servidor) desde el SDK nativo de la versión 2 de los Servicios de juego de Play (beta), que el cliente pasa al servidor. Luego, en el servidor, intercambia el código de autorización del servidor por un token de OAuth 2.0 que el servidor pueda usar para realizar llamadas a la API de Google Play Games Services.

Para obtener orientación adicional sobre cómo agregar autenticación a tus juegos, consulta Autenticación de la plataforma.

Sigue estos pasos para acceder sin conexión:

  1. En Google Play Console: Crea una credencial para tu servidor de juegos. El tipo de cliente de OAuth de la credencial será "Web".
  2. En la app para Android: Como parte de la autenticación de la plataforma, solicita un código de autorización del servidor para la credencial de este y pásalo al servidor. El PgsGamesSignInClient puede solicitar tres permisos de OAuth 2.0 cuando solicita acceso del servidor a las APIs web de los Servicios de juego de Play. Los alcances opcionales son PGS_AUTH_SCOPE_EMAIL, PGS_AUTH_SCOPE_PROFILE y PGS_AUTH_SCOPE_OPENID. Los dos alcances predeterminados son DRIVE_APPFOLDER y GAMES_LITE.
  3. En el servidor de tu juego: Intercambia el código de autorización del servidor por un token de acceso de OAuth con los servicios de autorización de Google y, luego, úsalo para llamar a las APIs de REST de los Servicios de juego de Play.

Antes de comenzar

Primero, deberás agregar tu juego en Google Play Console, como se describe en Configura los Servicios de juego de Google Play con tu juego.

Crea una app web del servidor

Los Servicios de juego de Google Play no ofrecen compatibilidad de backend para juegos web. Sin embargo, sí es compatible con el servidor de backend para el servidor de tu juego de Android.

Si deseas usar las APIs de REST para los Servicios de juego de Google Play en tu app del servidor, sigue estos pasos:

  1. En Google Play Console, selecciona un juego.
  2. Ve a Servicios de juego de Play > Configuración y administración > Configuración.
  3. Selecciona Agregar credencial para ir a la página Agregar credencial. Selecciona Servidor de juego como el tipo de credencial y continúa con la sección Autorización.
    1. Si el servidor del juego ya tiene un ID de cliente de OAuth, selecciónalo en el menú desplegable. Después de guardar los cambios, continúa con la siguiente sección.
    2. Si no tienes un ID de cliente de OAuth existente para el servidor de tu juego, puedes crear uno.
      1. Haz clic en Crear cliente de OAuth y sigue el vínculo Crear ID de cliente de OAuth.
      2. Esto te llevará a la página Crear ID de cliente de OAuth de Google Cloud Platform para el proyecto asociado a tu juego.
      3. Completa el formulario de la página y haz clic en Crear. Asegúrate de establecer el tipo de aplicación en Aplicación web.
      4. Regresa a la sección Autorización de la página Agregar credenciales, selecciona el cliente de OAuth recién creado y guarda los cambios.

Obtén el código de Auth del servidor

Para recuperar un código de autorización del servidor que tu juego pueda usar para tokens de acceso en tu servidor de backend, haz lo siguiente:

  1. Llama a PgsGamesSignInClient_requestServerSideAccess desde el cliente.
    1. Asegúrate de usar el ID de cliente de OAuth registrado para el servidor de tu juego, y no el de tu aplicación para Android.
    2. (Opcional) Si el servidor de tu juego requiere acceso sin conexión (acceso de larga duración mediante un token de actualización) a los Servicios de juego de Play, puedes establecer el parámetro force_refresh_token en verdadero.
  2. (Opcional) Como parte de la autenticación, los usuarios nuevos deberían ver una sola pantalla de consentimiento para los permisos adicionales. Cuando aceptas el consentimiento, estableces el parámetro PgsAuthScope scopes con los alcances de OAuth PGS_AUTH_SCOPE_EMAIL, PGS_AUTH_SCOPE_PROFILE y PGS_AUTH_SCOPE_OPENID. Si los usuarios rechazan el consentimiento, solo se envían los dos alcances predeterminados DRIVE_APPFOLDER y GAMES_LITE al backend.

    Pantalla de consentimiento para permisos de OAuth adicionales.
    Pantalla de consentimiento para permisos de OAuth adicionales. (haz clic para agrandar).

     // #include "google/games/pgs_games_sign_in_client.h"
     // 1. Define the Callback
     // This function is called when the server-side access request completes.
     // It provides the authorization code (on success) or an error (on failure).
     void OnServerSideAccessCallback(void* context, PgsError error, const char* serverAuthCode) {
         if (error == PgsError_Success) {
             if (serverAuthCode != nullptr) {
                 __android_log_print(ANDROID_LOG_INFO, "Games",
                     "Received Server Auth Code: %s", serverAuthCode);
                 // Send 'serverAuthCode' to your backend server immediately.
                 // Your server will exchange this code for an OAuth access token.
             }
         } else {
             __android_log_print(ANDROID_LOG_ERROR, "Games",
              "Failed to get server auth code. Error: %d", error);
         }
     }
     // 2. Define the Wrapper Function
     void RequestServerAccess(PgsGamesSignInClient* signInClient) {
         if (signInClient == nullptr) {
             return;
         }
         // This must match the "Web client ID" from your Google Cloud Console
         // (linked to your Play Console Game Server Credential).
         const char* SERVER_CLIENT_ID = "xxxx";
         // Set to 'true' if your server needs a Refresh Token (long-lived access).
         // Set to 'false' if you only need an Access Token (short-lived).
         bool forceRefreshToken = false;
         // Call the API
         PgsGamesSignInClient_requestServerSideAccess(
            signInClient,
            SERVER_CLIENT_ID,
            forceRefreshToken,
            OnServerSideAccessCallback, // The callback defined
            nullptr                     // User context (optional, passed to callback)
         );
     }
     // 3. Example Usage
     void TriggerSignInProcess(PgsGamesClient* gamesClient) {
          // Obtain the Sign-In Client from the main Games Client
          PgsGamesSignInClient* signInClient = PgsGamesClient_getSignInClient(gamesClient);
          RequestServerAccess(signInClient);
     }
     

  3. Envía el token de código de autorización de OAuth a tu servidor de backend para que este pueda intercambiarse, el ID del jugador se verifique con las APIs de REST de los Servicios de juego de Play y, luego, se autentique en tu juego.

Envía el código de Auth del servidor

Envía el código de Auth del servidor a tu servidor de backend para intercambiar tokens de acceso y actualización. Usa el token de acceso para llamar a la API de Play Games Services en nombre del jugador y, de manera opcional, almacena el token de actualización a fin de adquirir un token de acceso nuevo cuando este venza.

Para obtener más información sobre cómo funcionan los IDs de jugador, consulta IDs de jugador de nueva generación.

En el siguiente fragmento de código, se muestra cómo implementar el código del servidor en el lenguaje de programación C++ para intercambiar el código de autorización del servidor por tokens de acceso.

Java

/**
 * Exchanges the authcode for an access token credential. The credential
 * is associated with the given player.
 *
 * @param authCode - the non-null authcode passed from the client.
 * @param player   - the player object which the given authcode is
 *                 associated with.
 * @return the HTTP response code indicating the outcome of the exchange.
 */
private int exchangeAuthCode(String authCode, Player player) {
try {

    // The client_secret.json file is downloaded from the Google Cloud
    // console. This is used to identify your web application. The
    // contents of this file shouldn't be shared.

    File secretFile = new File("client_secret.json");

    // If we don't have the file, we can't access any APIs, so return
    // an error.
    if (!secretFile.exists()) {
        log("Secret file : " + secretFile
                .getAbsolutePath() + "  does not exist!");
        return HttpServletResponse.SC_FORBIDDEN;
    }

    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
            JacksonFactory.getDefaultInstance(), new
            FileReader(secretFile));

    // Extract the application ID of the game from the client ID.
    String applicationId = extractApplicationId(clientSecrets
            .getDetails().getClientId());

    GoogleTokenResponse tokenResponse =
            new GoogleAuthorizationCodeTokenRequest(
            HTTPTransport,
            JacksonFactory.getDefaultInstance(),
            "https://oauth2.googleapis.com/token",
            clientSecrets.getDetails().getClientId(),
            clientSecrets.getDetails().getClientSecret(),
            authCode,
            "")
            .execute();

    TokenVerifier(tokenResponse);

    log("hasRefresh == " + (tokenResponse.getRefreshToken() != null));
    log("Exchanging authCode: " + authCode + " for token");
    Credential credential = new Credential
            .Builder(BearerToken.authorizationHeaderAccessMethod())
            .setJsonFactory(JacksonFactory.getDefaultInstance())
            .setTransport(HTTPTransport)
            .setTokenServerEncodedUrl("https://www.googleapis.com/oauth2/v4/token")
            .setClientAuthentication(new HttpExecuteInterceptor() {
                @Override
                public void intercept(HttpRequest request)
                        throws IOException {
                        }
            })
            .build()
            .setFromTokenResponse(tokenResponse);

    player.setCredential(credential);

    // Now that we have a credential, we can access the Games API.
    PlayGamesAPI api = new PlayGamesAPI(player, applicationId,
            HTTPTransport, JacksonFactory.getDefaultInstance());

    // Call the verify method, which checks that the access token has
    // access to the Games API, and that the Player ID used by the
    // client matches the playerId associated with the accessToken.
    boolean ok = api.verifyPlayer();

    // Call a Games API on the server.
    if (ok) {
        ok = api.updatePlayerInfo();
        if (ok) {
            // persist the player.
            savePlayer(api.getPlayer());
        }
    }

    return ok ? HttpServletResponse.SC_OK :
            HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

  } catch (IOException e) {
    e.printStackTrace();
  }
  return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}

Puedes recuperar los permisos de OAuth con las bibliotecas cliente de las APIs de Google en Java o Python para obtener el objeto GoogleIdTokenVerifier. El siguiente fragmento de código muestra la implementación en el lenguaje de programación Java.

Java

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;

/**
 * Gets the GoogleIdTokenVerifier object and additional OAuth scopes.
 * If additional OAuth scopes are not requested, the idToken will be null.
 *
 * @param tokenResponse - the tokenResponse passed from the exchangeAuthCode
 *                        function.
 *
 **/

void TokenVerifier(GoogleTokenResponse tokenResponse) {

    string idTokenString = tokenResponse.getIdToken();

    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
        // Specify the WEB_CLIENT_ID of the app that accesses the backend:
        .setAudience(Collections.singletonList(WEB_CLIENT_ID))
        // Or, if multiple clients access the backend:
        //.setAudience(Arrays.asList(WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3))
        .build();

    GoogleIdToken idToken = verifier.verify(idTokenString);

    // The idToken can be null if additional OAuth scopes are not requested.
    if (idToken != null) {
        Payload payload = idToken.getPayload();

    // Print user identifier
    String userId = payload.getSubject();
    System.out.println("User ID: " + userId);

    // Get profile information from payload
    String email = payload.getEmail();
    boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
    String name = (String) payload.get("name");
    String pictureUrl = (String) payload.get("picture");
    String locale = (String) payload.get("locale");
    String familyName = (String) payload.get("family_name");
    String givenName = (String) payload.get("given_name");

    // This ID is unique to each Google Account, making it suitable for use as
    // a primary key during account lookup. Email is not a good choice because
    // it can be changed by the user.
    String sub = payload.getSubject();

    // Use or store profile information
    // ...

    } else {
      System.out.println("Invalid ID token.");
    }
}

Llama a las APIs de REST desde el servidor

Consulta las APIs de REST para los Servicios de juego de Google Play para obtener una descripción completa de las llamadas a la API disponibles.

Estos son algunos ejemplos de llamadas a la API de REST que pueden resultarte útiles:

Jugador

¿Deseas obtener el ID y los datos de perfil del jugador autenticado? Llama a Players.get con 'me' como ID.

Logros

Consulta la guía de logros para obtener más detalles.