game.js
const BACKEND_URL = "https://localhost/quizbattle";
const BACKEND_URL_API = BACKEND_URL + "/api";
var game = {};
var gameData = {};
var gameOptions = {
width: 0,
height: 0,
backgroundColor: 0xFFFFFF,
localStorageName: "__ab__"
};
var gameStuff = {
playerId: "",
playerName: "",
playerPic: "",
locale: ""
};
window.onload = function () {
$('#home-section').hide();
FBInstant.initializeAsync().then(function () {
FBInstant.startGameAsync().then(function () {
gameStuff.playerId = FBInstant.player.getID();
gameStuff.playerName = FBInstant.player.getName();
gameStuff.playerPic = FBInstant.player.getPhoto();
gameStuff.locale = FBInstant.getLocale();
gameStuff.contextType = FBInstant.context.getType();
gameOptions.height = window.innerHeight;
gameOptions.width = window.innerWidth / 2;
$('#u-photo-field').attr('src', gameStuff.playerPic);
$('#u-name-field').html(gameStuff.playerName);
$('#context-type').html(gameStuff.contextType);
$('#home-section').show();
loadingInitialData();
});
});
};
function getOrCreateContextId() {
var contextType = FBInstant.context.getType();
var contextId = FBInstant.context.getID();
console.log(contextType);
if (contextType == 'SOLO') {
contextId = FBInstant.player.getID() + '_SOLO';
}
return contextId;
}
function showMessage(div, message) {
document.getElementById(div).innerHTML = message;
}
function loadingInitialData() {
var contextId = getOrCreateContextId();
FBInstant.player.getSignedPlayerInfoAsync(contextId)
.then(function (signedPlayerInfo) {
// Requesting data from backend passing the signature as an argument
// The backend APP can retrieve contextId from the signature
var signature = signedPlayerInfo.getSignature();
return new backendClient(BACKEND_URL_API).load(contextId, signature);
})
.then(function (response) {
// Got successful response from backend
console.log('Loaded from backend', response);
if (response.empty) {
showMessage('test', 'No data');
} else {
showMessage('test', JSON.stringify(response.data));
}
}.bind(this))
.catch(function (error) {
// Got error response from backend
console.error('Not loaded from backend', error);
showMessage('error-messages', 'Error loading backend data:' + error.message);
}.bind(this));
}
function singlePlayBoot() {
}
function multiPlayBoot() {
}
function leaderBoardBoot() {
}
function helpBoot() {
}
node.js server side
var pg = require('pg');
var crypto = require('crypto-js');
module.exports = function(app) {
app.post('/get-match', function(request, response) {
var signature = request.body.signature;
var isValid = validate(signature);
if (isValid) {
var contextId = getEncodedData(signature);
loadMatchDataAsync(contextId)
.then(function(result){
if (result) {
response.json({'success':true, 'contextId':contextId, 'empty': false, 'data':result});
} else {
response.json({'success':true, 'contextId':contextId, 'empty': true});
}
})
.catch(function(err){
response.json({'success':false, 'error':err});
});
} else {
console.log('encoded data', getEncodedData(signature));
response.json({'success':false, 'error':'invalid signature'});
}
})
loadMatchDataAsync = function(contextId) {
return new Promise(function(resolve, reject){
pg.connect(process.env.DATABASE_URL, function(err, client, done) {
client.query('SELECT * FROM matches WHERE context = $1::text', [contextId], function(err, result) {
done();
if (err) {
reject(err);
}
if (result.rows.length > 0) {
resolve(result.rows[0].data);
} else {
resolve();
}
});
});
});
};
validate = function(signedRequest) {
try{
var firstpart = signedRequest.split('.')[0];
var replaced = firstpart.replace(/-/g, '+').replace(/_/g, '/');
var signature = crypto.enc.Base64.parse(replaced).toString();
const dataHash = crypto.HmacSHA256(signedRequest.split('.')[1], process.env.APP_SECRET).toString();
var isValid = signature === dataHash;
if (!isValid) {
console.log('Invalid signature');
console.log('firstpart', firstpart);
console.log('replaced ', replaced);
console.log('Expected', dataHash);
console.log('Actual', signature);
}
return isValid;
} catch (e) {
return false;
}
};
getEncodedData = function(signedRequest) {
try {
const json = crypto.enc.Base64.parse(signedRequest.split('.')[1]).toString(crypto.enc.Utf8);
const encodedData = JSON.parse(json);
/*
Here's an example of encodedData can look like
{
algorithm: 'HMAC-SHA256',
issued_at: 1520009634,
player_id: '123456789',
request_payload: 'backend_save'
}
*/
return encodedData.request_payload;
} catch (e) {
return null;
}
};
}
PHP server side
private function _validate($signedRequest) {
try {
list($encoded_signature, $payload) = explode('.', $signedRequest, 2);
// decode the data
$signature = base64_url_decode($encoded_signature);
//$data = json_decode(base64_url_decode($payload), true);
// confirm the signature
$expected_signature = hash_hmac('sha256', $payload, $this->facebook_config['app_secret'], $raw = true);
$isValid = $signature === $expected_signature;
if (!$isValid) {
error_log('Bad Signed JSON signature!');
return null;
}
return $isValid;
} catch (Exception $ex) {
return false;
}
}
private function _getEncodedData($signedRequest) {
try {
$encodedData = json_decode(base64_url_decode(explode('.', $signedRequest)[1]), true);
/*
$encodedData = {
"status":true,
"data":{
"algorithm":"HMAC-SHA256",
"issued_at":1552157956,
"player_id":"2522121991191915",
"request_payload":"2522121991191915_SOLO"
}
}
*/
return $encodedData;
} catch (Exception $ex) {
return null;
}
}
function load_post() {
$signature = $this->post('signature');
$isValid = $this->_validate($signature);
if ($isValid) {
$edata = $this->_getEncodedData($signature);
$this->success($edata->data);
} else {
$this->error('Invalid Request!');
}
..........................
}