* @link http://github.com/Nodge/yii2-eauth/ * @license http://www.opensource.org/licenses/bsd-license.php */ namespace nodge\eauth\oauth; use Yii; use OAuth\Common\Http\Uri\Uri; use OAuth\Common\Http\Client\ClientInterface; use OAuth\Common\Token\TokenInterface; use OAuth\Common\Storage\TokenStorageInterface; use nodge\eauth\EAuth; use nodge\eauth\IAuthService; use nodge\eauth\ErrorException; use yii\helpers\ArrayHelper; use yii\helpers\Url; /** * EOAuthService is a base class for all OAuth providers. * * @package application.extensions.eauth */ abstract class ServiceBase extends \nodge\eauth\ServiceBase implements IAuthService { /** * @var string Base url for API calls. */ protected $baseApiUrl; /** * @var int Default token lifetime. Used when service wont provide expires_in param. */ protected $tokenDefaultLifetime = null; /** * @var array TokenStorage class. Null means default value from EAuth component config. */ protected $tokenStorage; /** * @var array HttpClient class. Null means default value from EAuth component config. */ protected $httpClient; /** * @var TokenStorageInterface */ private $_tokenStorage; /** * @var ClientInterface */ private $_httpClient; /** * Initialize the component. * * @param EAuth $component the component instance. * @param array $options properties initialization. */ // public function init($component, $options = []) { // parent::init($component, $options); // } /** * For OAuth we can check existing access token. * Useful for API calls. * * @return bool * @throws ErrorException */ public function getIsAuthenticated() { if (!$this->authenticated) { try { $proxy = $this->getProxy(); $this->authenticated = $proxy->hasValidAccessToken(); } catch (\OAuth\Common\Exception\Exception $e) { throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e); } } return parent::getIsAuthenticated(); } /** * @return \nodge\eauth\oauth1\ServiceProxy|\nodge\eauth\oauth2\ServiceProxy */ abstract protected function getProxy(); /** * @return string the current url */ protected function getCallbackUrl() { return Url::to('', true); } /** * @param array $config */ public function setTokenStorage(array $config) { $this->tokenStorage = ArrayHelper::merge($this->tokenStorage, $config); } /** * @return TokenStorageInterface */ protected function getTokenStorage() { if (!isset($this->_tokenStorage)) { $config = $this->tokenStorage; if (!isset($config)) { $config = $this->getComponent()->getTokenStorage(); } $this->_tokenStorage = Yii::createObject($config); } return $this->_tokenStorage; } /** * @param array $config */ public function setHttpClient(array $config) { $this->httpClient = ArrayHelper::merge($this->httpClient, $config); } /** * @return ClientInterface */ protected function getHttpClient() { if (!isset($this->_httpClient)) { $config = $this->httpClient; if (!isset($config)) { $config = $this->getComponent()->getHttpClient(); } $this->_httpClient = Yii::createObject($config); } return $this->_httpClient; } /** * @return int */ public function getTokenDefaultLifetime() { return $this->tokenDefaultLifetime; } /** * Returns the protected resource. * * @param string $url url to request. * @param array $options HTTP request options. Keys: query, data, headers. * @param boolean $parseResponse Whether to parse response. * @return mixed the response. * @throws ErrorException */ public function makeSignedRequest($url, $options = [], $parseResponse = true) { if (!$this->getIsAuthenticated()) { throw new ErrorException(Yii::t('eauth', 'Unable to complete the signed request because the user was not authenticated.'), 401); } if (stripos($url, 'http') !== 0) { $url = $this->baseApiUrl . $url; } $url = new Uri($url); if (isset($options['query'])) { foreach ($options['query'] as $key => $value) { $url->addToQuery($key, $value); } } $data = isset($options['data']) ? $options['data'] : []; $method = !empty($data) ? 'POST' : 'GET'; $headers = isset($options['headers']) ? $options['headers'] : []; $response = $this->getProxy()->request($url, $method, $data, $headers); if ($parseResponse) { $response = $this->parseResponseInternal($response); } return $response; } /** * Returns the public resource. * * @param string $url url to request. * @param array $options HTTP request options. Keys: query, data, headers. * @param boolean $parseResponse Whether to parse response. * @return mixed the response. */ public function makeRequest($url, $options = [], $parseResponse = true) { if (stripos($url, 'http') !== 0) { $url = $this->baseApiUrl . $url; } $url = new Uri($url); if (isset($options['query'])) { foreach ($options['query'] as $key => $value) { $url->addToQuery($key, $value); } } $data = isset($options['data']) ? $options['data'] : []; $method = !empty($data) ? 'POST' : 'GET'; $headers = isset($options['headers']) ? $options['headers'] : []; $headers = array_merge($this->getProxy()->getExtraApiHeaders(), $headers); $response = $this->getHttpClient()->retrieveResponse($url, $data, $headers, $method); if ($parseResponse) { $response = $this->parseResponseInternal($response); } return $response; } /** * Parse response and check for errors. * * @param string $response * @return mixed * @throws ErrorException */ protected function parseResponseInternal($response) { try { $result = $this->parseResponse($response); if (!isset($result)) { throw new ErrorException(Yii::t('eauth', 'Invalid response format.'), 500); } $error = $this->fetchResponseError($result); if (isset($error) && !empty($error['message'])) { throw new ErrorException($error['message'], $error['code']); } return $result; } catch (\Exception $e) { throw new ErrorException($e->getMessage(), $e->getCode()); } } /** * @param string $response * @return mixed */ protected function parseResponse($response) { return json_decode($response, true); } /** * Returns the error array. * * @param array $response * @return array the error array with 2 keys: code and message. Should be null if no errors. */ protected function fetchResponseError($response) { if (isset($response['error'])) { return [ 'code' => 500, 'message' => 'Unknown error occurred.', ]; } return null; } /** * @return array|null An array with valid access_token information. */ protected function getAccessTokenData() { if (!$this->getIsAuthenticated()) { return null; } $token = $this->getProxy()->getAccessToken(); if (!isset($token)) { return null; } return [ 'access_token' => $token->getAccessToken(), 'refresh_token' => $token->getRefreshToken(), 'expires' => $token->getEndOfLife(), 'params' => $token->getExtraParams(), ]; } /** * @param array $data * @return string|null */ public function getAccessTokenResponseError($data) { return isset($data['error']) ? $data['error'] : null; } }