4

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!

Michael Samuel
  • 3,820
  • 12
  • 45
  • 85

1 Answers1

9

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 }

Khean
  • 99
  • 1
  • 13
Michael Samuel
  • 3,820
  • 12
  • 45
  • 85