* @link http://github.com/Nodge/yii2-eauth/ * @license http://www.opensource.org/licenses/bsd-license.php */ namespace nodge\eauth\oauth2; use Yii; use yii\helpers\Url; use OAuth\Common\Exception\Exception as OAuthException; use OAuth\Common\Http\Uri\Uri; use OAuth\Common\Consumer\Credentials; use OAuth\OAuth2\Service\ServiceInterface; use nodge\eauth\EAuth; use nodge\eauth\ErrorException; use nodge\eauth\IAuthService; use nodge\eauth\oauth\ServiceBase; /** * EOAuthService is a base class for all OAuth providers. * * @package application.extensions.eauth */ abstract class Service extends ServiceBase implements IAuthService { /** * @var string OAuth2 client id. */ protected $clientId; /** * @var string OAuth2 client secret key. */ protected $clientSecret; /** * @var array OAuth scopes. */ protected $scopes = []; /** * @var string */ protected $scopeSeparator = ' '; /** * @var array Provider options. Must contain the keys: authorize, access_token. */ protected $providerOptions = [ 'authorize' => '', 'access_token' => '', ]; /** * @var string Error key name in _GET options. */ protected $errorParam = 'error'; /** * @var string Error description key name in _GET options. */ protected $errorDescriptionParam = 'error_description'; /** * @var string Error code for access_denied response. */ protected $errorAccessDeniedCode = 'access_denied'; /** * @var string The display name for popup window. False to disable display mode. */ protected $popupDisplayName = 'popup'; /** * @var bool Whether to use the State param to improve security. */ protected $validateState = true; /** * @var ServiceProxy */ private $_proxy; /** * Initialize the component. * * @param EAuth $component the component instance. * @param array $options properties initialization. */ // public function init($component, $options = []) { // parent::init($component, $options); // } /** * @param string $id */ public function setClientId($id) { $this->clientId = $id; } /** * @param string $secret */ public function setClientSecret($secret) { $this->clientSecret = $secret; } /** * @param string|array $scopes */ public function setScope($scopes) { if (!is_array($scopes)) { $scopes = [$scopes]; } $resolvedScopes = []; $reflClass = new \ReflectionClass($this); $constants = $reflClass->getConstants(); foreach ($scopes as $scope) { $key = strtoupper('SCOPE_' . $scope); // try to find a class constant with this name if (array_key_exists($key, $constants)) { $resolvedScopes[] = $constants[$key]; } else { $resolvedScopes[] = $scope; } } $this->scopes = $resolvedScopes; } /** * @param bool $validate */ public function setValidateState($validate) { $this->validateState = $validate; } /** * @return bool */ public function getValidateState() { return $this->validateState; } /** * @return ServiceProxy */ protected function getProxy() { if (!isset($this->_proxy)) { $tokenStorage = $this->getTokenStorage(); $httpClient = $this->getHttpClient(); $credentials = new Credentials($this->clientId, $this->clientSecret, $this->getCallbackUrl()); $this->_proxy = new ServiceProxy($credentials, $httpClient, $tokenStorage, $this->scopes, null, $this); } return $this->_proxy; } /** * @return string the current url */ protected function getCallbackUrl() { if (isset($_GET['redirect_uri'])) { $url = $_GET['redirect_uri']; } else { $route = Yii::$app->getRequest()->getQueryParams(); array_unshift($route, ''); // Can not use these params in OAuth2 callbacks foreach (['code', 'state', 'redirect_uri'] as $param) { if (isset($route[$param])) { unset($route[$param]); } } $url = Url::to($route, true); } return $url; } /** * Authenticate the user. * * @return boolean whether user was successfuly authenticated. * @throws ErrorException */ public function authenticate() { if (!$this->checkError()) { return false; } try { $proxy = $this->getProxy(); if (!empty($_GET['code'])) { // This was a callback request from a service, get the token $proxy->requestAccessToken($_GET['code']); $this->authenticated = true; } else if ($proxy->hasValidAccessToken()) { $this->authenticated = true; } else { /** @var $url Uri */ $url = $proxy->getAuthorizationUri(); Yii::$app->getResponse()->redirect($url->getAbsoluteUri())->send(); } } catch (OAuthException $e) { throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e); } return $this->getIsAuthenticated(); } /** * Check request params for error code and message. * * @return bool * @throws ErrorException */ protected function checkError() { if (isset($_GET[$this->errorParam])) { $error_code = $_GET[$this->errorParam]; if ($error_code === $this->errorAccessDeniedCode) { // access_denied error (user canceled) $this->cancel(); } else { $error = $error_code; if (isset($_GET[$this->errorDescriptionParam])) { $error = $_GET[$this->errorDescriptionParam] . ' (' . $error . ')'; } throw new ErrorException($error); } return false; } return true; } /** * @return string */ public function getAuthorizationEndpoint() { $url = $this->providerOptions['authorize']; if ($this->popupDisplayName !== false && $this->getIsInsidePopup()) { $url = new Uri($url); $url->addToQuery('display', $this->popupDisplayName); $url = $url->getAbsoluteUri(); } return $url; } /** * @return string */ public function getAccessTokenEndpoint() { return $this->providerOptions['access_token']; } /** * @param string $response * @return array */ public function parseAccessTokenResponse($response) { return json_decode($response, true); } /** * @return array */ public function getAccessTokenArgumentNames() { return [ 'access_token' => 'access_token', 'expires_in' => 'expires_in', 'refresh_token' => 'refresh_token', ]; } /** * Return any additional headers always needed for this service implementation's OAuth calls. * * @return array */ public function getExtraOAuthHeaders() { return []; } /** * Return any additional headers always needed for this service implementation's API calls. * * @return array */ public function getExtraApiHeaders() { return []; } /** * Returns a class constant from ServiceInterface defining the authorization method used for the API * Header is the sane default. * * @return int */ public function getAuthorizationMethod() { return ServiceInterface::AUTHORIZATION_METHOD_HEADER_OAUTH; } /** * @return string */ public function getScopeSeparator() { return $this->scopeSeparator; } }