[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Part of the Joomla Framework OAuth1 Package 4 * 5 * @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved. 6 * @license GNU General Public License version 2 or later; see LICENSE 7 */ 8 9 namespace Joomla\OAuth1; 10 11 use Joomla\Application\SessionAwareWebApplicationInterface; 12 use Joomla\Http\Http; 13 use Joomla\Http\HttpFactory; 14 use Joomla\Input\Input; 15 use Joomla\Uri\Uri; 16 17 /** 18 * Joomla Framework class for interacting with an OAuth 1.0 and 1.0a server. 19 * 20 * @since 1.0 21 */ 22 abstract class Client 23 { 24 /** 25 * Options for the Client object. 26 * 27 * @var array|\ArrayAccess 28 * @since 1.0 29 */ 30 protected $options; 31 32 /** 33 * Contains access token key, secret and verifier. 34 * 35 * @var array 36 * @since 1.0 37 */ 38 protected $token = []; 39 40 /** 41 * The HTTP client object to use in sending HTTP requests. 42 * 43 * @var Http 44 * @since 1.0 45 */ 46 protected $client; 47 48 /** 49 * The input object to use in retrieving GET/POST data. 50 * 51 * @var Input 52 * @since 1.0 53 */ 54 protected $input; 55 56 /** 57 * The application object to send HTTP headers for redirects. 58 * 59 * @var SessionAwareWebApplicationInterface 60 * @since 1.0 61 */ 62 protected $application; 63 64 /** 65 * Selects which version of OAuth to use: 1.0 or 1.0a. 66 * 67 * @var string 68 * @since 1.0 69 */ 70 protected $version; 71 72 /** 73 * Constructor. 74 * 75 * @param SessionAwareWebApplicationInterface $application The application object 76 * @param Http $client The HTTP client object. 77 * @param Input $input The input object 78 * @param array|\ArrayAccess $options OAuth1 Client options. 79 * @param string $version Specify the OAuth version. By default we are using 1.0a. 80 * 81 * @since 1.0 82 */ 83 public function __construct( 84 SessionAwareWebApplicationInterface $application, 85 Http $client = null, 86 Input $input = null, 87 $options = [], 88 $version = '1.0a' 89 ) 90 { 91 if (!\is_array($options) && !($options instanceof \ArrayAccess)) 92 { 93 throw new \InvalidArgumentException( 94 'The options param must be an array or implement the ArrayAccess interface.' 95 ); 96 } 97 98 $this->application = $application; 99 $this->client = $client ?: HttpFactory::getHttp($options); 100 $this->input = $input ?: $application->getInput(); 101 $this->options = $options; 102 $this->version = $version; 103 } 104 105 /** 106 * Method to form the oauth flow. 107 * 108 * @return array|null The access token. 109 * 110 * @since 1.0 111 * @throws \DomainException 112 */ 113 public function authenticate() 114 { 115 // Already got some credentials stored? 116 if ($this->token) 117 { 118 $response = $this->verifyCredentials(); 119 120 if ($response) 121 { 122 return $this->token; 123 } 124 125 $this->token = null; 126 } 127 128 // Check for callback. 129 if (strcmp($this->version, '1.0a') === 0) 130 { 131 $verifier = $this->input->get('oauth_verifier'); 132 } 133 else 134 { 135 $verifier = $this->input->get('oauth_token'); 136 } 137 138 if (!empty($verifier)) 139 { 140 $session = $this->application->getSession(); 141 142 // Get token form session. 143 $this->token = [ 144 'key' => $session->get('oauth_token.key'), 145 'secret' => $session->get('oauth_token.secret'), 146 ]; 147 148 // Verify the returned request token. 149 if (strcmp($this->token['key'], $this->input->get('oauth_token')) !== 0) 150 { 151 throw new \DomainException('Bad session!'); 152 } 153 154 // Set token verifier for 1.0a. 155 if (strcmp($this->version, '1.0a') === 0) 156 { 157 $this->token['verifier'] = $this->input->get('oauth_verifier'); 158 } 159 160 // Generate access token. 161 $this->generateAccessToken(); 162 163 // Return the access token. 164 return $this->token; 165 } 166 167 // Generate a request token. 168 $this->generateRequestToken(); 169 170 // Authenticate the user and authorise the app. 171 $this->authorise(); 172 } 173 174 /** 175 * Method used to get a request token. 176 * 177 * @return void 178 * 179 * @since 1.1.2 180 * @throws \DomainException 181 */ 182 private function generateRequestToken() 183 { 184 $parameters = []; 185 186 // Set the callback URL. 187 if ($this->getOption('callback')) 188 { 189 $parameters['oauth_callback'] = $this->getOption('callback'); 190 } 191 192 // Make an OAuth request for the Request Token. 193 $response = $this->oauthRequest($this->getOption('requestTokenURL'), 'POST', $parameters); 194 195 parse_str($response->body, $params); 196 197 if (strcmp($this->version, '1.0a') === 0 && strcmp($params['oauth_callback_confirmed'], 'true') !== 0) 198 { 199 throw new \DomainException('Bad request token!'); 200 } 201 202 // Save the request token. 203 $this->token = ['key' => $params['oauth_token'], 'secret' => $params['oauth_token_secret']]; 204 205 // Save the request token in session 206 $session = $this->application->getSession(); 207 $session->set('oauth_token.key', $this->token['key']); 208 $session->set('oauth_token.secret', $this->token['secret']); 209 } 210 211 /** 212 * Method used to authorise the application. 213 * 214 * @return void 215 * 216 * @since 1.1.2 217 */ 218 private function authorise() 219 { 220 $url = $this->getOption('authoriseURL') . '?oauth_token=' . $this->token['key']; 221 222 if ($this->getOption('scope')) 223 { 224 $scope = \is_array($this->getOption('scope')) ? implode(' ', $this->getOption('scope')) : $this->getOption('scope'); 225 $url .= '&scope=' . urlencode($scope); 226 } 227 228 if ($this->getOption('sendheaders')) 229 { 230 $this->application->redirect($url); 231 } 232 } 233 234 /** 235 * Method used to get an access token. 236 * 237 * @return void 238 * 239 * @since 1.1.2 240 */ 241 private function generateAccessToken() 242 { 243 // Set the parameters. 244 $parameters = [ 245 'oauth_token' => $this->token['key'], 246 ]; 247 248 if (strcmp($this->version, '1.0a') === 0) 249 { 250 $parameters = array_merge($parameters, ['oauth_verifier' => $this->token['verifier']]); 251 } 252 253 // Make an OAuth request for the Access Token. 254 $response = $this->oauthRequest($this->getOption('accessTokenURL'), 'POST', $parameters); 255 256 parse_str($response->body, $params); 257 258 // Save the access token. 259 $this->token = ['key' => $params['oauth_token'], 'secret' => $params['oauth_token_secret']]; 260 } 261 262 /** 263 * Method used to make an OAuth request. 264 * 265 * @param string $url The request URL. 266 * @param string $method The request method. 267 * @param array $parameters Array containing request parameters. 268 * @param mixed $data The POST request data. 269 * @param array $headers An array of name-value pairs to include in the header of the request 270 * 271 * @return \Joomla\Http\Response 272 * 273 * @since 1.0 274 * @throws \DomainException 275 */ 276 public function oauthRequest($url, $method, $parameters, $data = [], $headers = []) 277 { 278 // Set the parameters. 279 $defaults = [ 280 'oauth_consumer_key' => $this->getOption('consumer_key'), 281 'oauth_signature_method' => 'HMAC-SHA1', 282 'oauth_version' => '1.0', 283 'oauth_nonce' => $this->generateNonce(), 284 'oauth_timestamp' => time(), 285 ]; 286 287 $parameters = array_merge($parameters, $defaults); 288 289 // Do not encode multipart parameters. Do not include $data in the signature if $data is not array. 290 if (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') !== false || !\is_array($data)) 291 { 292 $oauthHeaders = $parameters; 293 } 294 else 295 { 296 // Use all parameters for the signature. 297 $oauthHeaders = array_merge($parameters, $data); 298 } 299 300 // Sign the request. 301 $oauthHeaders = $this->signRequest($url, $method, $oauthHeaders); 302 303 // Get parameters for the Authorisation header. 304 if (\is_array($data)) 305 { 306 $oauthHeaders = array_diff_key($oauthHeaders, $data); 307 } 308 309 // Send the request. 310 switch ($method) 311 { 312 case 'GET': 313 $url = $this->toUrl($url, $data); 314 $response = $this->client->get($url, ['Authorization' => $this->createHeader($oauthHeaders)]); 315 316 break; 317 318 case 'POST': 319 $headers = array_merge($headers, ['Authorization' => $this->createHeader($oauthHeaders)]); 320 $response = $this->client->post($url, $data, $headers); 321 322 break; 323 324 case 'PUT': 325 $headers = array_merge($headers, ['Authorization' => $this->createHeader($oauthHeaders)]); 326 $response = $this->client->put($url, $data, $headers); 327 328 break; 329 330 case 'DELETE': 331 $headers = array_merge($headers, ['Authorization' => $this->createHeader($oauthHeaders)]); 332 $response = $this->client->delete($url, $headers); 333 334 break; 335 } 336 337 // Validate the response code. 338 $this->validateResponse($url, $response); 339 340 return $response; 341 } 342 343 /** 344 * Method to validate a response. 345 * 346 * @param string $url The request URL. 347 * @param Response $response The response to validate. 348 * 349 * @return void 350 * 351 * @since 1.0 352 * @throws \DomainException 353 */ 354 abstract public function validateResponse($url, $response); 355 356 /** 357 * Method used to create the header for the POST request. 358 * 359 * @param array $parameters Array containing request parameters. 360 * 361 * @return string The header. 362 * 363 * @since 1.1.2 364 */ 365 private function createHeader(array $parameters): string 366 { 367 $header = 'OAuth '; 368 369 foreach ($parameters as $key => $value) 370 { 371 if (!strcmp($header, 'OAuth ')) 372 { 373 $header .= $key . '="' . $this->safeEncode($value) . '"'; 374 } 375 else 376 { 377 $header .= ', ' . $key . '="' . $value . '"'; 378 } 379 } 380 381 return $header; 382 } 383 384 /** 385 * Method to create the URL formed string with the parameters. 386 * 387 * @param string $url The request URL. 388 * @param array $parameters Array containing request parameters. 389 * 390 * @return string The formed URL. 391 * 392 * @since 1.0 393 */ 394 public function toUrl($url, $parameters) 395 { 396 $uri = new Uri($url); 397 $uri->setQuery($parameters); 398 399 return (string) $uri; 400 } 401 402 /** 403 * Method used to sign requests. 404 * 405 * @param string $url The URL to sign. 406 * @param string $method The request method. 407 * @param array $parameters Array containing request parameters. 408 * 409 * @return array The array containing the request parameters, including signature. 410 * 411 * @since 1.1.2 412 */ 413 private function signRequest(string $url, string $method, array $parameters): array 414 { 415 // Create the signature base string. 416 $base = $this->baseString($url, $method, $parameters); 417 418 $parameters['oauth_signature'] = $this->safeEncode( 419 base64_encode( 420 hash_hmac('sha1', $base, $this->prepareSigningKey(), true) 421 ) 422 ); 423 424 return $parameters; 425 } 426 427 /** 428 * Prepare the signature base string. 429 * 430 * @param string $url The URL to sign. 431 * @param string $method The request method. 432 * @param array $parameters Array containing request parameters. 433 * 434 * @return string The base string. 435 * 436 * @since 1.1.2 437 */ 438 private function baseString(string $url, string $method, array $parameters): string 439 { 440 // Sort the parameters alphabetically 441 uksort($parameters, 'strcmp'); 442 443 // Encode parameters. 444 foreach ($parameters as $key => $value) 445 { 446 $key = $this->safeEncode($key); 447 448 if (\is_array($value)) 449 { 450 foreach ($value as $k => $v) 451 { 452 $v = $this->safeEncode($v); 453 $kv[] = "{$key}={$v}"; 454 } 455 } 456 else 457 { 458 $value = $this->safeEncode($value); 459 $kv[] = "{$key}={$value}"; 460 } 461 } 462 463 // Form the parameter string. 464 $params = implode('&', $kv); 465 466 // Signature base string elements. 467 $base = [ 468 $method, 469 $url, 470 $params, 471 ]; 472 473 // Return the base string. 474 return implode('&', $this->safeEncode($base)); 475 } 476 477 /** 478 * Encodes the string or array passed in a way compatible with OAuth. 479 * If an array is passed each array value will will be encoded. 480 * 481 * @param mixed $data The scalar or array to encode. 482 * 483 * @return string $data encoded in a way compatible with OAuth. 484 * 485 * @since 1.0 486 */ 487 public function safeEncode($data) 488 { 489 if (\is_array($data)) 490 { 491 return array_map([$this, 'safeEncode'], $data); 492 } 493 494 if (is_scalar($data)) 495 { 496 return str_ireplace( 497 ['+', '%7E'], 498 [' ', '~'], 499 rawurlencode($data) 500 ); 501 } 502 503 return ''; 504 } 505 506 /** 507 * Method used to generate the current nonce. 508 * 509 * @return string The current nonce. 510 * 511 * @since 1.0 512 */ 513 public static function generateNonce() 514 { 515 // The md5s look nicer than numbers. 516 return md5(microtime() . random_bytes(16)); 517 } 518 519 /** 520 * Prepares the OAuth signing key. 521 * 522 * @return string The prepared signing key. 523 * 524 * @since 1.1.2 525 */ 526 private function prepareSigningKey(): string 527 { 528 return $this->safeEncode($this->getOption('consumer_secret')) . '&' . $this->safeEncode(($this->token) ? $this->token['secret'] : ''); 529 } 530 531 /** 532 * Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful; 533 * returns a 401 status code and an error message if not. 534 * 535 * @return array The decoded JSON response 536 * 537 * @since 1.0 538 */ 539 abstract public function verifyCredentials(); 540 541 /** 542 * Get an option from the OAuth1 Client instance. 543 * 544 * @param string $key The name of the option to get 545 * @param mixed $default Optional default value if the option does not exist 546 * 547 * @return mixed The option value 548 * 549 * @since 1.0 550 */ 551 public function getOption($key, $default = null) 552 { 553 return $this->options[$key] ?? $default; 554 } 555 556 /** 557 * Set an option for the OAuth1 Client instance. 558 * 559 * @param string $key The name of the option to set 560 * @param mixed $value The option value to set 561 * 562 * @return $this 563 * 564 * @since 1.0 565 */ 566 public function setOption($key, $value) 567 { 568 $this->options[$key] = $value; 569 570 return $this; 571 } 572 573 /** 574 * Get the oauth token key or secret. 575 * 576 * @return array The oauth token key and secret. 577 * 578 * @since 1.0 579 */ 580 public function getToken() 581 { 582 return $this->token; 583 } 584 585 /** 586 * Set the oauth token. 587 * 588 * @param array $token The access token key and secret. 589 * 590 * @return $this 591 * 592 * @since 1.0 593 */ 594 public function setToken($token) 595 { 596 $this->token = $token; 597 598 return $this; 599 } 600 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |