Blame | Last modification | View Log | RSS feed
<?php/*** Copyright 2017 Facebook, Inc.** You are hereby granted a non-exclusive, worldwide, royalty-free license to* use, copy, modify, and distribute this software in source code or binary* form for use in connection with the web services and APIs provided by* Facebook.** As with any software that integrates with the Facebook platform, your use* of this software is subject to the Facebook Developer Principles and* Policies [http://developers.facebook.com/policy/]. This copyright notice* shall be included in all copies or substantial portions of the software.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER* DEALINGS IN THE SOFTWARE.**/namespace Facebook;use Facebook\Exceptions\FacebookSDKException;/*** Class SignedRequest** @package Facebook*/class SignedRequest{/*** @var FacebookApp The FacebookApp entity.*/protected $app;/*** @var string The raw encrypted signed request.*/protected $rawSignedRequest;/*** @var array The payload from the decrypted signed request.*/protected $payload;/*** Instantiate a new SignedRequest entity.** @param FacebookApp $facebookApp The FacebookApp entity.* @param string|null $rawSignedRequest The raw signed request.*/public function __construct(FacebookApp $facebookApp, $rawSignedRequest = null){$this->app = $facebookApp;if (!$rawSignedRequest) {return;}$this->rawSignedRequest = $rawSignedRequest;$this->parse();}/*** Returns the raw signed request data.** @return string|null*/public function getRawSignedRequest(){return $this->rawSignedRequest;}/*** Returns the parsed signed request data.** @return array|null*/public function getPayload(){return $this->payload;}/*** Returns a property from the signed request data if available.** @param string $key* @param mixed|null $default** @return mixed|null*/public function get($key, $default = null){if (isset($this->payload[$key])) {return $this->payload[$key];}return $default;}/*** Returns user_id from signed request data if available.** @return string|null*/public function getUserId(){return $this->get('user_id');}/*** Checks for OAuth data in the payload.** @return boolean*/public function hasOAuthData(){return $this->get('oauth_token') || $this->get('code');}/*** Creates a signed request from an array of data.** @param array $payload** @return string*/public function make(array $payload){$payload['algorithm'] = isset($payload['algorithm']) ? $payload['algorithm'] : 'HMAC-SHA256';$payload['issued_at'] = isset($payload['issued_at']) ? $payload['issued_at'] : time();$encodedPayload = $this->base64UrlEncode(json_encode($payload));$hashedSig = $this->hashSignature($encodedPayload);$encodedSig = $this->base64UrlEncode($hashedSig);return $encodedSig . '.' . $encodedPayload;}/*** Validates and decodes a signed request and saves* the payload to an array.*/protected function parse(){list($encodedSig, $encodedPayload) = $this->split();// Signature validation$sig = $this->decodeSignature($encodedSig);$hashedSig = $this->hashSignature($encodedPayload);$this->validateSignature($hashedSig, $sig);$this->payload = $this->decodePayload($encodedPayload);// Payload validation$this->validateAlgorithm();}/*** Splits a raw signed request into signature and payload.** @return array** @throws FacebookSDKException*/protected function split(){if (strpos($this->rawSignedRequest, '.') === false) {throw new FacebookSDKException('Malformed signed request.', 606);}return explode('.', $this->rawSignedRequest, 2);}/*** Decodes the raw signature from a signed request.** @param string $encodedSig** @return string** @throws FacebookSDKException*/protected function decodeSignature($encodedSig){$sig = $this->base64UrlDecode($encodedSig);if (!$sig) {throw new FacebookSDKException('Signed request has malformed encoded signature data.', 607);}return $sig;}/*** Decodes the raw payload from a signed request.** @param string $encodedPayload** @return array** @throws FacebookSDKException*/protected function decodePayload($encodedPayload){$payload = $this->base64UrlDecode($encodedPayload);if ($payload) {$payload = json_decode($payload, true);}if (!is_array($payload)) {throw new FacebookSDKException('Signed request has malformed encoded payload data.', 607);}return $payload;}/*** Validates the algorithm used in a signed request.** @throws FacebookSDKException*/protected function validateAlgorithm(){if ($this->get('algorithm') !== 'HMAC-SHA256') {throw new FacebookSDKException('Signed request is using the wrong algorithm.', 605);}}/*** Hashes the signature used in a signed request.** @param string $encodedData** @return string** @throws FacebookSDKException*/protected function hashSignature($encodedData){$hashedSig = hash_hmac('sha256',$encodedData,$this->app->getSecret(),$raw_output = true);if (!$hashedSig) {throw new FacebookSDKException('Unable to hash signature from encoded payload data.', 602);}return $hashedSig;}/*** Validates the signature used in a signed request.** @param string $hashedSig* @param string $sig** @throws FacebookSDKException*/protected function validateSignature($hashedSig, $sig){if (\hash_equals($hashedSig, $sig)) {return;}throw new FacebookSDKException('Signed request has an invalid signature.', 602);}/*** Base64 decoding which replaces characters:* + instead of -* / instead of _** @link http://en.wikipedia.org/wiki/Base64#URL_applications** @param string $input base64 url encoded input** @return string decoded string*/public function base64UrlDecode($input){$urlDecodedBase64 = strtr($input, '-_', '+/');$this->validateBase64($urlDecodedBase64);return base64_decode($urlDecodedBase64);}/*** Base64 encoding which replaces characters:* + instead of -* / instead of _** @link http://en.wikipedia.org/wiki/Base64#URL_applications** @param string $input string to encode** @return string base64 url encoded input*/public function base64UrlEncode($input){return strtr(base64_encode($input), '+/', '-_');}/*** Validates a base64 string.** @param string $input base64 value to validate** @throws FacebookSDKException*/protected function validateBase64($input){if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $input)) {throw new FacebookSDKException('Signed request contains malformed base64 encoding.', 608);}}}