PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/rainloop/v/0.0.0/app/libraries/Facebook/Entities/SignedRequest.php

https://gitlab.com/wuhang2003/rainloop-webmail
PHP | 386 lines | 164 code | 43 blank | 179 comment | 17 complexity | e27358708ce8bbaa30beabfdad75095e MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 2014 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\Entities;
  25. use Facebook\FacebookSDKException;
  26. use Facebook\FacebookSession;
  27. /**
  28. * Class SignedRequest
  29. * @package Facebook
  30. */
  31. class SignedRequest
  32. {
  33. /**
  34. * @var string
  35. */
  36. public $rawSignedRequest;
  37. /**
  38. * @var array
  39. */
  40. public $payload;
  41. /**
  42. * Instantiate a new SignedRequest entity.
  43. *
  44. * @param string|null $rawSignedRequest The raw signed request.
  45. * @param string|null $state random string to prevent CSRF.
  46. * @param string|null $appSecret
  47. */
  48. public function __construct($rawSignedRequest = null, $state = null, $appSecret = null)
  49. {
  50. if (!$rawSignedRequest) {
  51. return;
  52. }
  53. $this->rawSignedRequest = $rawSignedRequest;
  54. $this->payload = static::parse($rawSignedRequest, $state, $appSecret);
  55. }
  56. /**
  57. * Returns the raw signed request data.
  58. *
  59. * @return string|null
  60. */
  61. public function getRawSignedRequest()
  62. {
  63. return $this->rawSignedRequest;
  64. }
  65. /**
  66. * Returns the parsed signed request data.
  67. *
  68. * @return array|null
  69. */
  70. public function getPayload()
  71. {
  72. return $this->payload;
  73. }
  74. /**
  75. * Returns a property from the signed request data if available.
  76. *
  77. * @param string $key
  78. * @param mixed|null $default
  79. *
  80. * @return mixed|null
  81. */
  82. public function get($key, $default = null)
  83. {
  84. if (isset($this->payload[$key])) {
  85. return $this->payload[$key];
  86. }
  87. return $default;
  88. }
  89. /**
  90. * Returns user_id from signed request data if available.
  91. *
  92. * @return string|null
  93. */
  94. public function getUserId()
  95. {
  96. return $this->get('user_id');
  97. }
  98. /**
  99. * Checks for OAuth data in the payload.
  100. *
  101. * @return boolean
  102. */
  103. public function hasOAuthData()
  104. {
  105. return isset($this->payload['oauth_token']) || isset($this->payload['code']);
  106. }
  107. /**
  108. * Creates a signed request from an array of data.
  109. *
  110. * @param array $payload
  111. * @param string|null $appSecret
  112. *
  113. * @return string
  114. */
  115. public static function make(array $payload, $appSecret = null)
  116. {
  117. $payload['algorithm'] = 'HMAC-SHA256';
  118. $payload['issued_at'] = time();
  119. $encodedPayload = static::base64UrlEncode(json_encode($payload));
  120. $hashedSig = static::hashSignature($encodedPayload, $appSecret);
  121. $encodedSig = static::base64UrlEncode($hashedSig);
  122. return $encodedSig.'.'.$encodedPayload;
  123. }
  124. /**
  125. * Validates and decodes a signed request and returns
  126. * the payload as an array.
  127. *
  128. * @param string $signedRequest
  129. * @param string|null $state
  130. * @param string|null $appSecret
  131. *
  132. * @return array
  133. */
  134. public static function parse($signedRequest, $state = null, $appSecret = null)
  135. {
  136. list($encodedSig, $encodedPayload) = static::split($signedRequest);
  137. // Signature validation
  138. $sig = static::decodeSignature($encodedSig);
  139. $hashedSig = static::hashSignature($encodedPayload, $appSecret);
  140. static::validateSignature($hashedSig, $sig);
  141. // Payload validation
  142. $data = static::decodePayload($encodedPayload);
  143. static::validateAlgorithm($data);
  144. if ($state) {
  145. static::validateCsrf($data, $state);
  146. }
  147. return $data;
  148. }
  149. /**
  150. * Validates the format of a signed request.
  151. *
  152. * @param string $signedRequest
  153. *
  154. * @throws FacebookSDKException
  155. */
  156. public static function validateFormat($signedRequest)
  157. {
  158. if (strpos($signedRequest, '.') !== false) {
  159. return;
  160. }
  161. throw new FacebookSDKException(
  162. 'Malformed signed request.', 606
  163. );
  164. }
  165. /**
  166. * Decodes a raw valid signed request.
  167. *
  168. * @param string $signedRequest
  169. *
  170. * @returns array
  171. */
  172. public static function split($signedRequest)
  173. {
  174. static::validateFormat($signedRequest);
  175. return explode('.', $signedRequest, 2);
  176. }
  177. /**
  178. * Decodes the raw signature from a signed request.
  179. *
  180. * @param string $encodedSig
  181. *
  182. * @returns string
  183. *
  184. * @throws FacebookSDKException
  185. */
  186. public static function decodeSignature($encodedSig)
  187. {
  188. $sig = static::base64UrlDecode($encodedSig);
  189. if ($sig) {
  190. return $sig;
  191. }
  192. throw new FacebookSDKException(
  193. 'Signed request has malformed encoded signature data.', 607
  194. );
  195. }
  196. /**
  197. * Decodes the raw payload from a signed request.
  198. *
  199. * @param string $encodedPayload
  200. *
  201. * @returns array
  202. *
  203. * @throws FacebookSDKException
  204. */
  205. public static function decodePayload($encodedPayload)
  206. {
  207. $payload = static::base64UrlDecode($encodedPayload);
  208. if ($payload) {
  209. $payload = json_decode($payload, true);
  210. }
  211. if (is_array($payload)) {
  212. return $payload;
  213. }
  214. throw new FacebookSDKException(
  215. 'Signed request has malformed encoded payload data.', 607
  216. );
  217. }
  218. /**
  219. * Validates the algorithm used in a signed request.
  220. *
  221. * @param array $data
  222. *
  223. * @throws FacebookSDKException
  224. */
  225. public static function validateAlgorithm(array $data)
  226. {
  227. if (isset($data['algorithm']) && $data['algorithm'] === 'HMAC-SHA256') {
  228. return;
  229. }
  230. throw new FacebookSDKException(
  231. 'Signed request is using the wrong algorithm.', 605
  232. );
  233. }
  234. /**
  235. * Hashes the signature used in a signed request.
  236. *
  237. * @param string $encodedData
  238. * @param string|null $appSecret
  239. *
  240. * @return string
  241. *
  242. * @throws FacebookSDKException
  243. */
  244. public static function hashSignature($encodedData, $appSecret = null)
  245. {
  246. $hashedSig = hash_hmac(
  247. 'sha256', $encodedData, FacebookSession::_getTargetAppSecret($appSecret), $raw_output = true
  248. );
  249. if ($hashedSig) {
  250. return $hashedSig;
  251. }
  252. throw new FacebookSDKException(
  253. 'Unable to hash signature from encoded payload data.', 602
  254. );
  255. }
  256. /**
  257. * Validates the signature used in a signed request.
  258. *
  259. * @param string $hashedSig
  260. * @param string $sig
  261. *
  262. * @throws FacebookSDKException
  263. */
  264. public static function validateSignature($hashedSig, $sig)
  265. {
  266. if (mb_strlen($hashedSig) === mb_strlen($sig)) {
  267. $validate = 0;
  268. for ($i = 0; $i < mb_strlen($sig); $i++) {
  269. $validate |= ord($hashedSig[$i]) ^ ord($sig[$i]);
  270. }
  271. if ($validate === 0) {
  272. return;
  273. }
  274. }
  275. throw new FacebookSDKException(
  276. 'Signed request has an invalid signature.', 602
  277. );
  278. }
  279. /**
  280. * Validates a signed request against CSRF.
  281. *
  282. * @param array $data
  283. * @param string $state
  284. *
  285. * @throws FacebookSDKException
  286. */
  287. public static function validateCsrf(array $data, $state)
  288. {
  289. if (isset($data['state']) && $data['state'] === $state) {
  290. return;
  291. }
  292. throw new FacebookSDKException(
  293. 'Signed request did not pass CSRF validation.', 604
  294. );
  295. }
  296. /**
  297. * Base64 decoding which replaces characters:
  298. * + instead of -
  299. * / instead of _
  300. * @link http://en.wikipedia.org/wiki/Base64#URL_applications
  301. *
  302. * @param string $input base64 url encoded input
  303. *
  304. * @return string decoded string
  305. */
  306. public static function base64UrlDecode($input)
  307. {
  308. $urlDecodedBase64 = strtr($input, '-_', '+/');
  309. static::validateBase64($urlDecodedBase64);
  310. return base64_decode($urlDecodedBase64);
  311. }
  312. /**
  313. * Base64 encoding which replaces characters:
  314. * + instead of -
  315. * / instead of _
  316. * @link http://en.wikipedia.org/wiki/Base64#URL_applications
  317. *
  318. * @param string $input string to encode
  319. *
  320. * @return string base64 url encoded input
  321. */
  322. public static function base64UrlEncode($input)
  323. {
  324. return strtr(base64_encode($input), '+/', '-_');
  325. }
  326. /**
  327. * Validates a base64 string.
  328. *
  329. * @param string $input base64 value to validate
  330. *
  331. * @throws FacebookSDKException
  332. */
  333. public static function validateBase64($input)
  334. {
  335. $pattern = '/^[a-zA-Z0-9\/\r\n+]*={0,2}$/';
  336. if (preg_match($pattern, $input)) {
  337. return;
  338. }
  339. throw new FacebookSDKException(
  340. 'Signed request contains malformed base64 encoding.', 608
  341. );
  342. }
  343. }