I'm currently developing an instant game on Facebook and need to verify that the player info is authentic from facebook. Facebook provides verification through a hashed signature as documented here:
https://developers.facebook.com/docs/games/instant-games/sdk/fbinstant6.2#signedplayerinfo
I followed the steps as outlined but can't ever get the signature to match:
This is the signature:
$_POST['psig'] = 'je3yV8uKmysDrjXv1xp_RY2rTMJLEREM7xj8SGt5HEk.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZF9hdCI6MTU1MjI1NTc2OCwicGxheWVyX2lkIjoiMjA1OTE4OTA2MDgyMzk4MyIsInJlcXVlc3RfcGF5bG9hZCI6InBkYXRhIn0';
1) Split the signature into two parts delimited by the '.' character.
$first_part = explode('.', $_POST['psig'])[0];
echo $first_part;
// Output:
je3yV8uKmysDrjXv1xp_RY2rTMJLEREM7xj8SGt5HEk
$second_part = explode('.', $_POST['psig'])[1];
echo $second_part;
// Output
eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImlzc3VlZF9hdCI6MTU1MjI1NTc2OCwicGxheWVyX2lkIjoiMjA1OTE4OTA2MDgyMzk4MyIsInJlcXVlc3RfcGF5bG9hZCI6InBkYXRhIn0
2) Decode the first part (the encoded signature) with base64url encoding.
echo base64_decode($first_part);
// Output:
���Wˊ�+�5��Qcj�0��DC;�?�G
3) Decode the second part (the response payload) with base64url encoding, which should be a string representation of a JSON object that has the following fields: ** algorithm - always equals to HMAC-SHA256 ** issued_at - a unix timestamp of when this response was issued. ** player_id - unique identifier of the player. ** request_payload - the requestPayload string you specified when calling FBInstant.player.getSignedPlayerInfoAsync.
$payload = base64_decode($second_part);
echo $payload;
// Output:
{"algorithm":"HMAC-SHA256","issued_at":1552255768,"player_id":"2059189060823983","request_payload":"pdata"}
4) Hash the whole response payload string using HMAC SHA-256 and your app secret and confirm that it is equal to the encoded signature.
$check = hash_hmac('sha256', $payload, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', true);
echo $check;
// Output:
n4Q�Kމ`<�?�����tT�~x����L�
I have spent few hours with this and can't figure out what I'm doing wrong!
Finally got it!
I had 2 mistakes:
1) When decoding the encoded signature to check against, base64decode URL should be used NOT the regular base64decode. So code in step 2 should be like this:
$first_part = base64_decode(str_replace(array('-', '_'), array('+', '/'), $first_part));
2) The generated hash should have the ORIGINAL encoded response payload
string NOT the decoded one. The doc is not very clear about this. So it should be like this:
$check = hash_hmac('sha256', $second_part, $app_secret, true);
By doing the above, the decoded signature would equal to the binary data in the generated hash :)
And finally to check if signature valid:
if($first_part == $check){
//valid
}