Prevent Cheating for Instant Tournaments

Instant Tournaments is a feature that helps Facebook users discover and play games with friends and others through Feed, building connections with each other through lightweight competition. By integrating with our anti cheat feature, games can prevent bad actors from posting illegitimate scores to the leaderboard and harming the overall experience for other participants.

This feature is currently in Beta testing and is not yet available yet for public use. While it is included in our SDKs, access to this feature is restricted to our Beta test partners at this time.

Overview

On tournament creation, a tournament can be flagged as requiring score validation. On updating a score for a tournament through the normal FBInstant SDK flow, a signed request to a developer-set endpoint would request the developer to confirm the validity of the player and score by returning a boolean response to whether the game accepts or rejects the score for the given user. Once confirmation of the score is received, we would then post the score to the leaderboard and resolve the promise.

Implementation Breakdown

1. Configure “Score Validation Callback URL” on Developer Site

On the Developer Site, configure a callback URL which will be the address where a HTTP POST request is made when the instant tournament score posting flow is invoked. The developer then responds to this request with a boolean verifying the validity of a player’s score. This URL must be a secure HTTPS URL. This can be found under the Instant Tournaments tab.

Request

On updating a tournament score via tournament.postScoreAsync, a HTTP POST request is made to your specified ScoreValidation Callback URL. This POST request will contain some parameters, including the signed_request is base64url encoded and signed with an HMAC version of your App Secret, based on the OAuth 2.0 spec.

{
  "signed_request": "SIGNED-REQUEST"
}

Once the signed request is parsed and decoded it contains JSON data, structured as follows:

{
  "game_id":"GAME-APP-ID>",
  "player_id":"PLAYER-ID",
  "tournament_id":"TOURNAMENT-ID",
  "score": 10,
}

Response

You must respond with 200 OK to complete the callback, and your response must use the secure HTTPS protocol.

Expected response:

{
  "valid_score": true,
}

2. Pass “forceScoreValidation” boolean on tournament creation

On tournament creation through the FBInstant SDK, set the forceScoreValidation parameter to true to specify if the tournament should require game server validation before a score can be added or updated on the leaderboard. Scores that are posted through postSessionScore, postSessionScoreAsync, and tournament.postScore without confirmation from the Score Validation Callback will be rejected.

Note, you may not want to configure forced score validation for all instant tournaments created for your app. This is to your discretion.

FBInstant.tournament.createAsync({
    initialScore: 4,
    data: { tournamentLevel: 'hard' },
    config: {
      title: 'Level X Tournament',
      forceScoreValidation: true,
      image: base64Picture,
      sortOrder: 'HIGHER_IS_BETTER',
      scoreFormat: 'NUMERIC',
      endTime: 1515806355,
    },
  })
  .then(function() {
     // user successfully created tournament and switch into tournament context.
  });

3. Update score posting to tournaments

For instant tournaments where forceScoreValidation is enabled, a new webhook event will be triggered when a tournament score is attempted to be added or updated on the leaderboard. The game will decode the signed request, and either confirm or reject the score before the score will be posted to the tournament.

If we do not receive a response from the callback URL, we will assume positive intent and update the score to the leaderboard. The timeout for the response will be 10s.

FBInstant.tournament.postScoreAsync(3)
  .then(function(){
     // score has been confirmed from game server. continue with the game.
  });

4. Remove score parameter from tournament.shareAsync

For tournaments where forceScoreValidation is enabled, the score parameter has become optional for tournament.shareAsync. This API can now be utilized behind a button, or continued to be used as a pop-up dialog after gameplay session.

FBInstant.tournament.shareAsync({
    data: { tournamentLevel: 'hard' },
  })
  .then(function() {
     // user successfully shared the tournament.
  });