Серверный доступ к игровым сервисам Google Play

Мы рекомендуем использовать GamesSignInClient для аутентификации игроков и безопасной передачи их идентификационных данных на внутренний сервер. Это позволит вашей игре безопасно получать идентификационные данные игрока и другие данные, не подвергаясь потенциальному вмешательству при прохождении через устройство.

После успешной аутентификации игрока вы можете запросить специальный одноразовый код (называемый кодом авторизации сервера ) из SDK Play Games Services v2, который клиент передаёт серверу. Затем на сервере обменяйте код авторизации сервера на токен OAuth 2.0, который сервер сможет использовать для вызовов API Google Play Games Services.

Дополнительные рекомендации по добавлению аутентификации в игры см. в разделе Аутентификация платформы для игр Android .

Для автономного доступа необходимо выполнить следующие шаги:

  1. В Google Play Console: создайте учётные данные для вашего игрового сервера. Тип клиента OAuth для учётных данных — «web».
  2. В приложении для Android: в рамках аутентификации платформы запросите код авторизации сервера для учётных данных вашего сервера и передайте его на сервер. GamesSigninClient может запросить три области действия OAuth 2.0 при запросе доступа к веб-API игровых сервисов на стороне сервера. Необязательные области действия: EMAIL , PROFILE и OPEN_ID . Две области действия по умолчанию: DRIVE_APPFOLDER и GAMES_LITE .
  3. На игровом сервере: обменяйте код авторизации сервера на токен доступа OAuth с помощью служб авторизации Google, а затем используйте его для вызова API REST игровых сервисов Play.

Прежде чем начать

Сначала вам нужно добавить свою игру в Google Play Console , как описано в разделе Настройка игровых сервисов Google Play , и интегрировать аутентификацию платформы игровых сервисов Play с вашей игрой.

Создайте серверное веб-приложение

Сервисы Google Play Game не предоставляют бэкенд-поддержку для веб-игр. Однако они предоставляют бэкенд-поддержку для сервера вашей игры на Android.

Если вы хотите использовать REST API для сервисов Google Play Игр в своем серверном приложении, выполните следующие действия:

  1. В консоли Google Play выберите игру.
  2. Перейдите в раздел «Сервисы Play Игр» > «Настройка и управление» > «Конфигурация» .
  3. Выберите «Добавить учётные данные» , чтобы перейти на страницу «Добавить учётные данные» . Выберите «Игровой сервер» в качестве типа учётных данных и перейдите в раздел «Авторизация» .
    1. Если на вашем игровом сервере уже есть идентификатор клиента OAuth, выберите его в выпадающем меню. После сохранения изменений перейдите к следующему разделу .
    2. Если у вас нет существующего идентификатора клиента OAuth для вашего игрового сервера, вы можете его создать.
      1. Нажмите «Создать OAuth-клиент» и перейдите по ссылке «Создать OAuth-идентификатор клиента» .
      2. Это приведет вас на страницу создания идентификатора клиента OAuth на платформе Google Cloud Platform для вашего проекта, связанного с вашей игрой.
      3. Заполните форму страницы и нажмите «Создать». Убедитесь, что тип приложения — «Веб-приложение».
      4. Вернитесь в раздел «Авторизация» на странице «Добавление учетных данных» , выберите недавно созданный клиент OAuth и сохраните изменения.

Получить код авторизации сервера

Чтобы получить код авторизации сервера, который ваша игра сможет использовать для токенов доступа на вашем внутреннем сервере:

  1. Вызовите requestServerSideAccess с клиента.
    1. Убедитесь, что вы используете идентификатор клиента OAuth, зарегистрированный для вашего игрового сервера , а не идентификатор клиента OAuth вашего приложения Android.
    2. (Необязательно) Если вашему игровому серверу требуется автономный доступ (долговременный доступ с использованием токена обновления) к игровым сервисам Play, вы можете установить для параметра forceRefreshToken значение true.
  2. (Необязательно) В рамках аутентификации новые пользователи должны видеть единый экран согласия для дополнительных областей действия. После принятия согласия вы устанавливаете параметр scopes с областями действия OAuth EMAIL , PROFILE и OPEN_ID . Если пользователи отклоняют согласие, на серверную часть отправляются только две области действия по умолчанию: DRIVE_APPFOLDER и GAMES_LITE .

    Экран согласия для дополнительных областей OAuth.
    Экран согласия для дополнительных областей OAuth. (кликните для увеличения).

     GamesSignInClient gamesSignInClient = PlayGames.getGamesSignInClient(this);
     gamesSignInClient.requestServerSideAccess(OAUTH_2_WEB_CLIENT_ID, /* forceRefreshToken= / false,
         / Additional AuthScope */ scopes)
       .addOnCompleteListener( task -> {
         if (task.isSuccessful()) {
           AuthResponse authresp = task.getResult();
           // Send the authorization code as a string and a
           // list of the granted AuthScopes that were granted by the
           // user. Exchange for an access token.
           // Verify the player with Play Games Services REST APIs.
         } else {
           // Failed to retrieve authentication code.
         }
     });
     

  3. Отправьте токен кода авторизации OAuth на ваш внутренний сервер для обмена, проверки идентификатора игрока с помощью API REST игровых сервисов Play, а затем аутентификации в вашей игре.

Отправьте код авторизации сервера

Отправьте код авторизации сервера на ваш внутренний сервер для обмена на токены доступа и обновления. Используйте токен доступа для вызова API игровых сервисов Play от имени игрока и, при необходимости, сохраните токен обновления для получения нового токена доступа по истечении срока действия текущего токена доступа.

Дополнительную информацию о работе идентификаторов игроков см. в разделе Идентификаторы игроков следующего поколения .

В следующем фрагменте кода показано, как можно реализовать серверный код на языке программирования 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 API
    // 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;
}

Вы можете получить области действия OAuth, используя клиентские библиотеки Google API на Java или Python, чтобы получить объект GoogleIdTokenVerifier . Следующий фрагмент кода демонстрирует реализацию на языке программирования 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");

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

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

Вызов REST API с сервера

Полное описание доступных вызовов API см. в разделе API REST для сервисов Google Play Игр .

Примеры вызовов REST API, которые могут оказаться вам полезными, включают следующее:

Игрок

Хотите получить аутентификацию по ID игрока и данным профиля? Вызовите Players.get , указав 'me' в качестве ID.

Друзья

Подробности смотрите в руководстве по сериалу «Друзья» .

  • Чтобы получить список друзей игрока, вызовите Players.list, указав friends_all в качестве collection .

  • Чтобы проверить, есть ли у вас доступ к списку друзей, вызовите Players.get me в качестве playerID , и просмотрите поле profileSettings.friendsListVisibility в ответе.

Достижения

Подробности смотрите в руководстве по достижениям .

  • Чтобы получить список текущих достижений, вызовите AchievementDefinitions.list .

  • Объедините это с вызовом Achievements.list , чтобы узнать, какие достижения разблокировал игрок.

  • Вызовите Achievements.unlock , чтобы разблокировать достижение игрока.

  • Вызовите Achievements.increment , чтобы сообщить о ходе выполнения достижения и узнать, разблокировал ли игрок его.

  • Если вы отлаживаете игру, которая еще не вышла в эксплуатацию, вы можете вызвать Achievements.reset или Achievements.resetAll из API управления, чтобы сбросить достижения в исходное состояние.

Таблицы лидеров

Подробности смотрите в руководстве по таблицам лидеров .

  • Чтобы получить список всех таблиц результатов в игре, вызовите Leaderboards.list .

  • Если игрок закончил игру, вы можете отправить его счет на Scores.submit и узнать, является ли он новым рекордом.

  • Чтобы отобразить таблицу лидеров, получите данные из Scores.list и покажите их пользователю.

  • Используйте Scores.listWindow для поиска набора оценок, близких к наивысшему результату пользователя.

  • Чтобы получить дополнительную информацию об очках игрока в конкретной таблице лидеров (например, входит ли игрок в 12% лучших игроков), вызовите Scores.get .

  • Если вы отлаживаете игру, вы можете вызвать Scores.reset из API управления, чтобы сбросить все очки для этого игрока из конкретной таблицы лидеров.