PHP 30
Facebook SignedRequest Guest on 24th April 2021 11:20:04 PM
  1. <?php
  2. /**
  3.  * Copyright  Facebook, Inc.
  4.  *
  5.  * You are hereby granted a non-exclusive, worldwide, royalty-free license to
  6.  * use, copy, modify, and distribute this software in source code or binary
  7.  * form for use in connection with the web services and APIs provided by
  8.  * Facebook.
  9.  *
  10.  * As with any software that integrates with the Facebook platform, your use
  11.  * of this software is subject to the Facebook Developer Principles and
  12.  * Policies [http://developers.facebook.com/policy/]. This copyright notice
  13.  * shall be included in all copies or substantial portions of the software.
  14.  *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20.  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21.  * DEALINGS IN THE SOFTWARE.
  22.  *
  23.  */
  24. namespace Facebook;
  25.  
  26. use Facebook\Exceptions\FacebookSDKException;
  27.  
  28. /**
  29.  * Class SignedRequest
  30.  *
  31.  * @package Facebook
  32.  */
  33. class SignedRequest
  34. {
  35.     /**
  36.      * @var FacebookApp The FacebookApp entity.
  37.      */
  38.     protected $app;
  39.  
  40.     /**
  41.      * @var string The raw encrypted signed request.
  42.      */
  43.     protected $rawSignedRequest;
  44.  
  45.     /**
  46.      * @var array The payload from the decrypted signed request.
  47.      */
  48.     protected $payload;
  49.  
  50.     /**
  51.      * Instantiate a new SignedRequest entity.
  52.      *
  53.      * @param FacebookApp $facebookApp      The FacebookApp entity.
  54.      * @param string|null $rawSignedRequest The raw signed request.
  55.      */
  56.     public function __construct(FacebookApp $facebookApp, $rawSignedRequest = null)
  57.     {
  58.         $this->app = $facebookApp;
  59.  
  60.         if (!$rawSignedRequest) {
  61.             return;
  62.         }
  63.  
  64.         $this->rawSignedRequest = $rawSignedRequest;
  65.  
  66.         $this->parse();
  67.     }
  68.  
  69.     /**
  70.      * Returns the raw signed request data.
  71.      *
  72.      * @return string|null
  73.      */
  74.     public function getRawSignedRequest()
  75.     {
  76.         return $this->rawSignedRequest;
  77.     }
  78.  
  79.     /**
  80.      * Returns the parsed signed request data.
  81.      *
  82.      * @return array|null
  83.      */
  84.     public function getPayload()
  85.     {
  86.         return $this->payload;
  87.     }
  88.  
  89.     /**
  90.      * Returns a property from the signed request data if available.
  91.      *
  92.      * @param string     $key
  93.      * @param mixed|null $default
  94.      *
  95.      * @return mixed|null
  96.      */
  97.     public function get($key, $default = null)
  98.     {
  99.         if (isset($this->payload[$key])) {
  100.             return $this->payload[$key];
  101.         }
  102.  
  103.         return $default;
  104.     }
  105.  
  106.     /**
  107.      * Returns user_id from signed request data if available.
  108.      *
  109.      * @return string|null
  110.      */
  111.     public function getUserId()
  112.     {
  113.         return $this->get('user_id');
  114.     }
  115.  
  116.     /**
  117.      * Checks for OAuth data in the payload.
  118.      *
  119.      * @return boolean
  120.      */
  121.     public function hasOAuthData()
  122.     {
  123.         return $this->get('oauth_token') || $this->get('code');
  124.     }
  125.  
  126.     /**
  127.      * Creates a signed request from an array of data.
  128.      *
  129.      * @param array $payload
  130.      *
  131.      * @return string
  132.      */
  133.     public function make(array $payload)
  134.     {
  135.         $payload['algorithm'] = isset($payload['algorithm']) ? $payload['algorithm'] : 'HMAC-SHA256';
  136.         $payload['issued_at'] = isset($payload['issued_at']) ? $payload['issued_at'] : time();
  137.         $encodedPayload = $this->base64UrlEncode(json_encode($payload));
  138.  
  139.         $hashedSig = $this->hashSignature($encodedPayload);
  140.         $encodedSig = $this->base64UrlEncode($hashedSig);
  141.  
  142.         return $encodedSig . '.' . $encodedPayload;
  143.     }
  144.  
  145.     /**
  146.      * Validates and decodes a signed request and saves
  147.      * the payload to an array.
  148.      */
  149.     protected function parse()
  150.     {
  151.         list($encodedSig, $encodedPayload) = $this->split();
  152.  
  153.         // Signature validation
  154.         $sig = $this->decodeSignature($encodedSig);
  155.         $hashedSig = $this->hashSignature($encodedPayload);
  156.         $this->validateSignature($hashedSig, $sig);
  157.  
  158.         $this->payload = $this->decodePayload($encodedPayload);
  159.  
  160.         // Payload validation
  161.         $this->validateAlgorithm();
  162.     }
  163.  
  164.     /**
  165.      * Splits a raw signed request into signature and payload.
  166.      *
  167.      * @return array
  168.      *
  169.      * @throws FacebookSDKException
  170.      */
  171.     protected function split()
  172.     {
  173.         if (strpos($this->rawSignedRequest, '.') === false) {
  174.             throw new FacebookSDKException('Malformed signed request.', 606);
  175.         }
  176.  
  177.         return explode('.', $this->rawSignedRequest, 2);
  178.     }
  179.  
  180.     /**
  181.      * Decodes the raw signature from a signed request.
  182.      *
  183.      * @param string $encodedSig
  184.      *
  185.      * @return string
  186.      *
  187.      * @throws FacebookSDKException
  188.      */
  189.     protected function decodeSignature($encodedSig)
  190.     {
  191.         $sig = $this->base64UrlDecode($encodedSig);
  192.  
  193.         if (!$sig) {
  194.             throw new FacebookSDKException('Signed request has malformed encoded signature data.', 607);
  195.         }
  196.  
  197.         return $sig;
  198.     }
  199.  
  200.     /**
  201.      * Decodes the raw payload from a signed request.
  202.      *
  203.      * @param string $encodedPayload
  204.      *
  205.      * @return array
  206.      *
  207.      * @throws FacebookSDKException
  208.      */
  209.     protected function decodePayload($encodedPayload)
  210.     {
  211.         $payload = $this->base64UrlDecode($encodedPayload);
  212.  
  213.         if ($payload) {
  214.             $payload = json_decode($payload, true);
  215.         }
  216.  
  217.         if (!is_array($payload)) {
  218.             throw new FacebookSDKException('Signed request has malformed encoded payload data.', 607);
  219.         }
  220.  
  221.         return $payload;
  222.     }
  223.  
  224.     /**
  225.      * Validates the algorithm used in a signed request.
  226.      *
  227.      * @throws FacebookSDKException
  228.      */
  229.     protected function validateAlgorithm()
  230.     {
  231.         if ($this->get('algorithm') !== 'HMAC-SHA256') {
  232.             throw new FacebookSDKException('Signed request is using the wrong algorithm.', 605);
  233.         }
  234.     }
  235.  
  236.     /**
  237.      * Hashes the signature used in a signed request.
  238.      *
  239.      * @param string $encodedData
  240.      *
  241.      * @return string
  242.      *
  243.      * @throws FacebookSDKException
  244.      */
  245.     protected function hashSignature($encodedData)
  246.     {
  247.         $hashedSig = hash_hmac(
  248.             'sha256',
  249.             $encodedData,
  250.             $this->app->getSecret(),
  251.             $raw_output = true
  252.         );
  253.  
  254.         if (!$hashedSig) {
  255.             throw new FacebookSDKException('Unable to hash signature from encoded payload data.', 602);
  256.         }
  257.  
  258.         return $hashedSig;
  259.     }
  260.  
  261.     /**
  262.      * Validates the signature used in a signed request.
  263.      *
  264.      * @param string $hashedSig
  265.      * @param string $sig
  266.      *
  267.      * @throws FacebookSDKException
  268.      */
  269.     protected function validateSignature($hashedSig, $sig)
  270.     {
  271.         if (\hash_equals($hashedSig, $sig)) {
  272.             return;
  273.         }
  274.  
  275.         throw new FacebookSDKException('Signed request has an invalid signature.', 602);
  276.     }
  277.  
  278.     /**
  279.      * Base64 decoding which replaces characters:
  280.      *   + instead of -
  281.      *   / instead of _
  282.      *
  283.      * @link http://en.wikipedia.org/wiki/Base64#URL_applications
  284.      *
  285.      * @param string $input base64 url encoded input
  286.      *
  287.      * @return string decoded string
  288.      */
  289.     public function base64UrlDecode($input)
  290.     {
  291.         $urlDecodedBase64 = strtr($input, '-_', '+/');
  292.         $this->validateBase64($urlDecodedBase64);
  293.  
  294.         return base64_decode($urlDecodedBase64);
  295.     }
  296.  
  297.     /**
  298.      * Base64 encoding which replaces characters:
  299.      *   + instead of -
  300.      *   / instead of _
  301.      *
  302.      * @link http://en.wikipedia.org/wiki/Base64#URL_applications
  303.      *
  304.      * @param string $input string to encode
  305.      *
  306.      * @return string base64 url encoded input
  307.      */
  308.     public function base64UrlEncode($input)
  309.     {
  310.         return strtr(base64_encode($input), '+/', '-_');
  311.     }
  312.  
  313.     /**
  314.      * Validates a base64 string.
  315.      *
  316.      * @param string $input base64 value to validate
  317.      *
  318.      * @throws FacebookSDKException
  319.      */
  320.     protected function validateBase64($input)
  321.     {
  322.         if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $input)) {
  323.             throw new FacebookSDKException('Signed request contains malformed base64 encoding.', 608);
  324.         }
  325.     }
  326. }

Paste-bin is for source code and general debugging text.

Login or Register to edit, delete and keep track of your pastes and more.

Raw Paste

Login or Register to edit or fork this paste. It's free.