Açıklama Yok

oauth2.php 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <?php
  2. use NSL\Persistent\Persistent;
  3. require_once NSL_PATH . '/includes/auth.php';
  4. abstract class NextendSocialOauth2 extends NextendSocialAuth {
  5. const CSRF_LENGTH = 32;
  6. protected $state = false;
  7. protected $client_id;
  8. protected $client_secret;
  9. protected $redirect_uri;
  10. protected $endpointAuthorization;
  11. protected $endpointAccessToken;
  12. protected $endpointRestAPI;
  13. protected $defaultRestParams = array();
  14. protected $scopes = array();
  15. public function checkError() {
  16. if (isset($_REQUEST['error']) && isset($_REQUEST['error_description'])) {
  17. if ($this->validateState()) {
  18. throw new Exception($_REQUEST['error'] . ': ' . htmlspecialchars_decode($_REQUEST['error_description']));
  19. }
  20. }
  21. }
  22. public function getTestUrl() {
  23. return $this->endpointAccessToken;
  24. }
  25. public function hasAuthenticateData() {
  26. return isset($_REQUEST['code']);
  27. }
  28. /**
  29. * @param string $client_id
  30. */
  31. public function setClientId($client_id) {
  32. $this->client_id = $client_id;
  33. }
  34. /**
  35. * @param string $client_secret
  36. */
  37. public function setClientSecret($client_secret) {
  38. $this->client_secret = $client_secret;
  39. }
  40. /**
  41. * @param string $redirect_uri
  42. */
  43. public function setRedirectUri($redirect_uri) {
  44. $this->redirect_uri = $redirect_uri;
  45. }
  46. public function getEndpointAuthorization() {
  47. return $this->endpointAuthorization;
  48. }
  49. /*
  50. * Adds response_type, client_id, redirect_uri and state as query parameter in the Authorization Url.
  51. * client_id can be found in the App when you create one
  52. * redirect_uri is the url you wish to be redirected after you entered you login credentials
  53. * state is a randomly generated string
  54. */
  55. public function createAuthUrl() {
  56. $args = array(
  57. 'response_type' => 'code',
  58. 'client_id' => urlencode($this->client_id),
  59. 'redirect_uri' => urlencode($this->redirect_uri),
  60. 'state' => urlencode($this->getState())
  61. );
  62. $scopes = apply_filters('nsl_' . $this->providerID . '_scopes', $this->scopes);
  63. if (count($scopes)) {
  64. $args['scope'] = urlencode($this->formatScopes($scopes));
  65. }
  66. $args = apply_filters('nsl_' . $this->providerID . '_auth_url_args', $args);
  67. return add_query_arg($args, $this->getEndpointAuthorization());
  68. }
  69. /**
  70. * @param $scopes
  71. * Connects an array of scopes with whitespace.
  72. *
  73. * @return string
  74. */
  75. protected function formatScopes($scopes) {
  76. return implode(' ', array_unique($scopes));
  77. }
  78. /**
  79. * @return bool|false|string
  80. * If the code that was sent by the selected provider and the state is valid,
  81. * we can make a request for an accessToken with wp_remote_post().
  82. * The result contains HTTP headers and content.
  83. *
  84. * Returns the accessToken with which we can make certain requests for their user profile data.
  85. * @throws Exception
  86. */
  87. public function authenticate() {
  88. if (isset($_GET['code'])) {
  89. if (!$this->validateState()) {
  90. throw new Exception('Unable to validate CSRF state');
  91. }
  92. $http_args = array(
  93. 'timeout' => 15,
  94. 'user-agent' => 'WordPress',
  95. 'body' => array(
  96. 'grant_type' => 'authorization_code',
  97. 'code' => $_GET['code'],
  98. 'redirect_uri' => $this->redirect_uri,
  99. 'client_id' => $this->client_id,
  100. 'client_secret' => $this->client_secret
  101. )
  102. );
  103. $request = wp_remote_post($this->endpointAccessToken, $this->extendAllHttpArgs($http_args));
  104. if (is_wp_error($request)) {
  105. throw new Exception($request->get_error_message());
  106. } else if (wp_remote_retrieve_response_code($request) !== 200) {
  107. $this->errorFromResponse(json_decode(wp_remote_retrieve_body($request), true));
  108. }
  109. $accessTokenData = json_decode(wp_remote_retrieve_body($request), true);
  110. if (!is_array($accessTokenData)) {
  111. throw new Exception(sprintf(__('Unexpected response: %s', 'nextend-facebook-connect'), wp_remote_retrieve_body($request)));
  112. }
  113. $accessTokenData['created'] = time();
  114. $this->access_token_data = $accessTokenData;
  115. return wp_json_encode($accessTokenData);
  116. }
  117. return false;
  118. }
  119. /**
  120. * @param $response
  121. *
  122. * @throws Exception
  123. */
  124. protected function errorFromResponse($response) {
  125. if (isset($response['error'])) {
  126. throw new Exception($response['error'] . ': ' . $response['error_description']);
  127. }
  128. }
  129. public function deleteLoginPersistentData() {
  130. Persistent::delete($this->providerID . '_state');
  131. }
  132. /**
  133. * If the stored state is the same as the state we have received from the remote Provider, it is valid.
  134. *
  135. * @return bool
  136. */
  137. protected function validateState() {
  138. $this->state = Persistent::get($this->providerID . '_state');
  139. if ($this->state === false) {
  140. return false;
  141. }
  142. if (empty($_GET['state'])) {
  143. return false;
  144. }
  145. if ($_GET['state'] == $this->state) {
  146. return true;
  147. }
  148. return false;
  149. }
  150. /**
  151. * Returns the stored state for the current provider.
  152. *
  153. * @return bool|mixed|null|string
  154. */
  155. protected function getState() {
  156. $this->state = Persistent::get($this->providerID . '_state');
  157. if ($this->state === null) {
  158. $this->state = $this->generateRandomState();
  159. Persistent::set($this->providerID . '_state', $this->state);
  160. }
  161. return $this->state;
  162. }
  163. /**
  164. * Generates a random string, which will be needed for the remote provider.
  165. * It will be stored for a time.
  166. *
  167. * @return bool|string
  168. */
  169. protected function generateRandomState() {
  170. if (function_exists('random_bytes')) {
  171. return $this->bytesToString(random_bytes(self::CSRF_LENGTH));
  172. }
  173. if (function_exists('mcrypt_create_iv')) {
  174. /** @noinspection PhpDeprecationInspection */
  175. $binaryString = mcrypt_create_iv(self::CSRF_LENGTH, MCRYPT_DEV_URANDOM);
  176. if ($binaryString !== false) {
  177. return $this->bytesToString($binaryString);
  178. }
  179. }
  180. if (function_exists('openssl_random_pseudo_bytes')) {
  181. $wasCryptographicallyStrong = false;
  182. $binaryString = openssl_random_pseudo_bytes(self::CSRF_LENGTH, $wasCryptographicallyStrong);
  183. if ($binaryString !== false && $wasCryptographicallyStrong === true) {
  184. return $this->bytesToString($binaryString);
  185. }
  186. }
  187. return $this->randomStr(self::CSRF_LENGTH);
  188. }
  189. private function bytesToString($binaryString) {
  190. return substr(bin2hex($binaryString), 0, self::CSRF_LENGTH);
  191. }
  192. private function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
  193. $str = '';
  194. $max = strlen($keyspace) - 1;
  195. for ($i = 0; $i < $length; ++$i) {
  196. $str .= $keyspace[random_int(0, $max)];
  197. }
  198. return $str;
  199. }
  200. /**
  201. * @param $path
  202. * @param array $data
  203. * @param $endpoint
  204. *
  205. * @return array
  206. * @throws Exception
  207. */
  208. public function get($path, $data = array(), $endpoint = false) {
  209. $http_args = array(
  210. 'timeout' => 15,
  211. 'user-agent' => 'WordPress',
  212. 'body' => array_merge($this->defaultRestParams, $data)
  213. );
  214. if (!$endpoint) {
  215. $endpoint = $this->endpointRestAPI;
  216. }
  217. $request = wp_remote_get($endpoint . $path, $this->extendHttpArgs($this->extendAllHttpArgs($http_args)));
  218. if (is_wp_error($request)) {
  219. throw new Exception($request->get_error_message());
  220. } else if (wp_remote_retrieve_response_code($request) !== 200) {
  221. $this->errorFromResponse(json_decode(wp_remote_retrieve_body($request), true));
  222. }
  223. $result = json_decode(wp_remote_retrieve_body($request), true);
  224. if (!is_array($result)) {
  225. throw new Exception(sprintf(__('Unexpected response: %s', 'nextend-facebook-connect'), wp_remote_retrieve_body($request)));
  226. }
  227. return $result;
  228. }
  229. /**
  230. * @param $http_args
  231. * Puts additional data into the http header.
  232. * Used for getting access to the resources with a bearer token.
  233. *
  234. * @return mixed
  235. */
  236. protected function extendHttpArgs($http_args) {
  237. $http_args['headers'] = array(
  238. 'Authorization' => 'Bearer ' . $this->access_token_data['access_token']
  239. );
  240. return $http_args;
  241. }
  242. protected function extendAllHttpArgs($http_args) {
  243. return $http_args;
  244. }
  245. }