├── .gitignore ├── src ├── assets │ ├── images │ │ ├── auth.png │ │ └── auth-src.png │ ├── WidgetAssetBundle.php │ ├── js │ │ └── eauth.js │ └── css │ │ └── eauth.css ├── ErrorException.php ├── Bootstrap.php ├── messages │ ├── blank │ │ └── eauth.php │ ├── en │ │ └── eauth.php │ ├── de │ │ └── eauth.php │ ├── ro │ │ └── eauth.php │ ├── pl │ │ └── eauth.php │ ├── uk │ │ └── eauth.php │ └── ru │ │ └── eauth.php ├── services │ ├── extended │ │ ├── GitHubOAuth2Service.php │ │ ├── MailruOAuth2Service.php │ │ ├── TwitterOAuth1Service.php │ │ ├── OdnoklassnikiOAuth2Service.php │ │ ├── VKontakteOAuth2Service.php │ │ └── FacebookOAuth2Service.php │ ├── YahooOpenIDService.php │ ├── YandexOAuth2Service.php │ ├── SteamOpenIDService.php │ ├── TwitterOAuth1Service.php │ ├── LinkedinOAuth1Service.php │ ├── LinkedinOAuth2Service.php │ ├── InstagramOAuth2Service.php │ ├── MailruOAuth2Service.php │ ├── LiveOAuth2Service.php │ ├── GitHubOAuth2Service.php │ ├── FacebookOAuth2Service.php │ ├── OdnoklassnikiOAuth2Service.php │ ├── VKontakteOAuth2Service.php │ └── GoogleOAuth2Service.php ├── openid │ ├── ControllerBehavior.php │ └── Service.php ├── views │ ├── widget.php │ └── redirect.php ├── RedirectWidget.php ├── Widget.php ├── IAuthService.php ├── oauth1 │ ├── Service.php │ └── ServiceProxy.php ├── oauth │ ├── SessionTokenStorage.php │ ├── ServiceBase.php │ └── HttpClient.php ├── oauth2 │ ├── ServiceProxy.php │ └── Service.php ├── EAuth.php └── ServiceBase.php ├── composer.json ├── LICENSE ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | composer.phar -------------------------------------------------------------------------------- /src/assets/images/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nodge/yii2-eauth/HEAD/src/assets/images/auth.png -------------------------------------------------------------------------------- /src/assets/images/auth-src.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nodge/yii2-eauth/HEAD/src/assets/images/auth-src.png -------------------------------------------------------------------------------- /src/ErrorException.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | class ErrorException extends \yii\base\ErrorException 13 | { 14 | 15 | } -------------------------------------------------------------------------------- /src/Bootstrap.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | use Yii; 13 | use yii\base\Application; 14 | use yii\base\BootstrapInterface; 15 | 16 | /** 17 | * This is the bootstrap class for the yii2-eauth extension. 18 | */ 19 | class Bootstrap implements BootstrapInterface 20 | { 21 | /** 22 | * @inheritdoc 23 | */ 24 | public function bootstrap($app) 25 | { 26 | Yii::setAlias('@eauth', __DIR__); 27 | } 28 | } -------------------------------------------------------------------------------- /src/assets/WidgetAssetBundle.php: -------------------------------------------------------------------------------- 1 | 14 | * @since 2.0 15 | */ 16 | class WidgetAssetBundle extends AssetBundle 17 | { 18 | public $sourcePath = '@eauth/assets'; 19 | public $css = [ 20 | 'css/eauth.css', 21 | ]; 22 | public $js = [ 23 | 'js/eauth.js', 24 | ]; 25 | public $depends = [ 26 | 'yii\web\JqueryAsset', 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /src/messages/blank/eauth.php: -------------------------------------------------------------------------------- 1 | '{service}', 5 | 'Invalid response http code: {code}.' => '{code}', 6 | 'Invalid response format.' => '', 7 | 'Unable to complete the authentication because the required data was not received.' => '{provider}', 8 | 'Unable to complete the request because the user was not authenticated.' => '', 9 | 10 | 'Redirecting back to the application...' => '', 11 | 'Click here to return to the application.' => '', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Yandex', 16 | 'VK.com' => 'VK.com', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Moikrug.ru', 20 | 'Odnoklassniki' => 'Odnoklassniki', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodge/yii2-eauth", 3 | "description": "Yii2 EAuth Extension. EAuth allows to authenticate users with accounts on other websites (Google, Facebook, Twitter, etc).", 4 | "keywords": ["yii2", "extension", "eauth", "openid", "oauth", "authentication"], 5 | "homepage": "https://github.com/Nodge/yii2-eauth", 6 | "type": "yii2-extension", 7 | "license": "New BSD License", 8 | "authors": [ 9 | { 10 | "name": "Maxim Zemskov", 11 | "email": "nodge@yandex.ru", 12 | "homepage": "http://nodge.ru/" 13 | } 14 | ], 15 | "support": { 16 | "source": "https://github.com/nodge/yii2-eauth" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "nodge\\eauth\\": "src/" 21 | } 22 | }, 23 | "require": { 24 | "php": ">=5.4.0", 25 | "lib-curl": "*", 26 | "yiisoft/yii2": "*", 27 | "lusitanian/oauth": "~0.3.0", 28 | "nodge/lightopenid": "~1.1.0" 29 | }, 30 | "extra": { 31 | "bootstrap": "nodge\\eauth\\Bootstrap" 32 | } 33 | } -------------------------------------------------------------------------------- /src/services/extended/GitHubOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services\extended; 11 | 12 | class GitHubOAuth2Service extends \nodge\eauth\services\GitHubOAuth2Service 13 | { 14 | 15 | protected function fetchAttributes() 16 | { 17 | $info = $this->makeSignedRequest('user'); 18 | 19 | $this->attributes['id'] = $info['id']; 20 | $this->attributes['name'] = $info['login']; 21 | $this->attributes['url'] = $info['html_url']; 22 | 23 | $this->attributes['following'] = $info['following']; 24 | $this->attributes['followers'] = $info['followers']; 25 | $this->attributes['public_repos'] = $info['public_repos']; 26 | $this->attributes['public_gists'] = $info['public_gists']; 27 | $this->attributes['avatar_url'] = $info['avatar_url']; 28 | 29 | return true; 30 | } 31 | } -------------------------------------------------------------------------------- /src/messages/en/eauth.php: -------------------------------------------------------------------------------- 1 | 'Undefined service name: {service}.', 5 | 'Invalid response http code: {code}.' => 'Invalid response http code: {code}.', 6 | 'Invalid response format.' => 'Invalid response format.', 7 | 'Unable to complete the authentication because the required data was not received.' => 'Unable to complete the authentication because the required data was not received.', 8 | 'Unable to complete the request because the user was not authenticated.' => 'Unable to complete the request because the user was not authenticated.', 9 | 10 | 'Redirecting back to the application...' => 'Redirecting back to the application...', 11 | 'Click here to return to the application.' => 'Click here to return to the application.', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Yandex', 16 | 'VK.com' => 'VK.com', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Moikrug.ru', 20 | 'Odnoklassniki' => 'Odnoklassniki', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; -------------------------------------------------------------------------------- /src/messages/de/eauth.php: -------------------------------------------------------------------------------- 1 | 'Undefinierter Servicename: {service}', 5 | 'Invalid response http code: {code}.' => 'Ungültiger HTTP Code: {code}', 6 | 'Invalid response format.' => 'Ungültiges Antwortformat.', 7 | 'Unable to complete the authentication because the required data was not received.' => 'Die Authentifizierung konnte nicht durchgeführt werden, da keine Daten empfangen wurden.', 8 | 'Unable to complete the request because the user was not authenticated.' => 'Die Anfrage konnte nicht durchgeführt werden, da der Nutzer nicht authentifiziert war.', 9 | 10 | 'Redirecting back to the application...' => 'Weiterleitung zur Applikation...', 11 | 'Click here to return to the application.' => 'Klicke hier um zur Applikation zurückzukehren', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Yandex', 16 | 'VK.com' => 'VK.com', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Moikrug.ru', 20 | 'Odnoklassniki' => 'Odnoklassniki', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; -------------------------------------------------------------------------------- /src/messages/ro/eauth.php: -------------------------------------------------------------------------------- 1 | 'Serviciu nedefinit: {service}.', 5 | 'Invalid response http code: {code}.' => 'Codul http al răspunsului este invalid: {code}.', 6 | 'Invalid response format.' => 'Formatul răspunsului este invalid.', 7 | 'Unable to complete the authentication because the required data was not received.' => 'Nu s-a putut finaliza autentificarea pentru că datele necesare nu au fost primite.', 8 | 'Unable to complete the request because the user was not authenticated.' => 'Cererea nu a fost finalizată pentru că utilizatorul nu este autentificat.', 9 | 10 | 'Redirecting back to the application...' => 'Te redirecționăm înapoi la aplicație...', 11 | 'Click here to return to the application.' => 'Apasă aici pentru a te întoarce la aplicație.', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Yandex', 16 | 'VK.com' => 'VK.com', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Moikrug.ru', 20 | 'Odnoklassniki' => 'Odnoklassniki', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; 24 | -------------------------------------------------------------------------------- /src/openid/ControllerBehavior.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\openid; 11 | 12 | use Yii; 13 | use yii\base\Action; 14 | use yii\base\ActionFilter; 15 | 16 | /** 17 | * @package application.extensions.eauth 18 | */ 19 | class ControllerBehavior extends ActionFilter 20 | { 21 | /** 22 | * This method is invoked right before an action is to be executed (after all possible filters.) 23 | * You may override this method to do last-minute preparation for the action. 24 | * 25 | * @param Action $action the action to be executed. 26 | * @return boolean whether the action should continue to be executed. 27 | */ 28 | public function beforeAction($action) 29 | { 30 | $request = Yii::$app->getRequest(); 31 | 32 | if (in_array($request->getBodyParam('openid_mode', ''), ['id_res', 'cancel'])) { 33 | $request->enableCsrfValidation = false; 34 | } 35 | 36 | return parent::beforeAction($action); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/views/widget.php: -------------------------------------------------------------------------------- 1 | $assetBundle])->register($this); 14 | 15 | // Open the authorization dilalog in popup window. 16 | if ($popup) { 17 | $options = []; 18 | foreach ($services as $name => $service) { 19 | $options[$service->id] = $service->jsArguments; 20 | } 21 | $this->registerJs('$("#' . $id . '").eauth(' . json_encode($options) . ');'); 22 | } 23 | 24 | ?> 25 |
26 | 38 |
39 | -------------------------------------------------------------------------------- /src/services/extended/MailruOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services\extended; 11 | 12 | class MailruOAuth2Service extends \nodge\eauth\services\MailruOAuth2Service 13 | { 14 | 15 | protected function fetchAttributes() 16 | { 17 | $tokenData = $this->getAccessTokenData(); 18 | 19 | $info = $this->makeSignedRequest('/', [ 20 | 'query' => [ 21 | 'uids' => $tokenData['params']['x_mailru_vid'], 22 | 'method' => 'users.getInfo', 23 | 'app_id' => $this->clientId, 24 | ], 25 | ]); 26 | 27 | $info = $info[0]; 28 | 29 | $this->attributes['id'] = $info['uid']; 30 | $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name']; 31 | $this->attributes['first_name'] = $info['first_name']; 32 | $this->attributes['last_name'] = $info['last_name']; 33 | $this->attributes['url'] = $info['link']; 34 | $this->attributes['photo'] = $info['pic']; 35 | 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/messages/pl/eauth.php: -------------------------------------------------------------------------------- 1 | 'Nieznana nazwa usługi: {service}.', 5 | 'Invalid response http code: {code}.' => 'Nieprawidłowy kod HTTP odpowiedzi: {code}.', 6 | 'Invalid response format.' => 'Nieprawidłowy format odpowiedzi.', 7 | 'Unable to complete the authentication because the required data was not received.' => 'Nie można dokonać uwierzytelniania, ponieważ nie otrzymano wymaganych danych.', 8 | 'Unable to complete the request because the user was not authenticated.' => 'Nie można przetworzyć żądania, ponieważ użytkownik nie został uwierzytelniony.', 9 | 10 | 'Redirecting back to the application...' => 'Przekierowuję z powrotem do aplikacji...', 11 | 'Click here to return to the application.' => 'Kliknij tutaj, aby powrócić do aplikacji.', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Yandex', 16 | 'VK.com' => 'VK.com', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Moikrug.ru', 20 | 'Odnoklassniki' => 'Odnoklassniki', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; 24 | -------------------------------------------------------------------------------- /src/messages/uk/eauth.php: -------------------------------------------------------------------------------- 1 | 'Авторизація за допомогою {service} не підтримується.', 5 | 'Invalid response http code: {code}.' => 'Невірна відповідь від сервера авторизації: {code}.', 6 | 'Invalid response format.' => 'Сервер авторизації повернув данні в невірному форматі.', 7 | 'Unable to complete the authentication because the required data was not received.' => 'Неможливо завершити авторизацію користувача, так як {provider} не передає необхідну інформацію.', 8 | 'Unable to complete the request because the user was not authenticated.' => 'Неможливо виконати захищений запит, так як користувач не був авторизований.', 9 | 10 | 'Redirecting back to the application...' => 'Redirecting back to the application...', 11 | 'Click here to return to the application.' => 'Click here to return to the application.', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Яндекс', 16 | 'VK.com' => 'ВКонтакте', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Мой круг', 20 | 'Odnoklassniki' => 'Одноклассники', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; 24 | -------------------------------------------------------------------------------- /src/views/redirect.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 29 | 30 | 31 | 32 | 35 | 36 | 41 | 42 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/messages/ru/eauth.php: -------------------------------------------------------------------------------- 1 | 'Авторизация с помощью {service} не поддерживается.', 5 | 'Invalid response http code: {code}.' => 'Неверный ответ от сервера авторизации: {code}.', 6 | 'Invalid response format.' => 'Сервер авторизации вернул данные в неправильном формате.', 7 | 'Unable to complete the authentication because the required data was not received.' => 'Невозможно завершить авторизацию пользователя, потому что {provider} не передает необходимую информацию.', 8 | 'Unable to complete the request because the user was not authenticated.' => 'Невозможно выполнить защищенный запрос, потому что пользователь не был авторизован.', 9 | 10 | 'Redirecting back to the application...' => 'Перенаправление обратно в приложение...', 11 | 'Click here to return to the application.' => 'Нажмите сюда чтобы вернуться обратно в приложение.', 12 | 13 | 'Google' => 'Google', 14 | 'Twitter' => 'Twitter', 15 | 'Yandex' => 'Яндекс', 16 | 'VK.com' => 'ВКонтакте', 17 | 'Facebook' => 'Facebook', 18 | 'Mail.ru' => 'Mail.ru', 19 | 'Moikrug.ru' => 'Мой круг', 20 | 'Odnoklassniki' => 'Одноклассники', 21 | 'LinkedIn' => 'LinkedIn', 22 | 'GitHub' => 'GitHub', 23 | ]; -------------------------------------------------------------------------------- /src/RedirectWidget.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | use \Yii; 13 | use yii\helpers\ArrayHelper; 14 | 15 | /** 16 | * The EAuthRedirectWidget widget displays the redirect page after returning from provider. 17 | * 18 | * @package application.extensions.eauth 19 | */ 20 | class RedirectWidget extends Widget 21 | { 22 | 23 | /** 24 | * @var mixed the widget mode. Default to "login". 25 | */ 26 | public $url = null; 27 | 28 | /** 29 | * @var boolean whether to use redirect inside the popup window. 30 | */ 31 | public $redirect = true; 32 | 33 | /** 34 | * @var string 35 | */ 36 | public $view = 'redirect'; 37 | 38 | /** 39 | * @var array 40 | */ 41 | public $params = []; 42 | 43 | /** 44 | * Executes the widget. 45 | */ 46 | public function run() 47 | { 48 | echo $this->render($this->view, 49 | ArrayHelper::merge([ 50 | 'id' => $this->getId(), 51 | 'url' => $this->url, 52 | 'redirect' => $this->redirect, 53 | ], $this->params) 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Maxim Zemskov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/services/YahooOpenIDService.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services; 11 | 12 | use nodge\eauth\openid\Service; 13 | 14 | /** 15 | * Yahoo provider class. 16 | * 17 | * @package application.extensions.eauth.services 18 | */ 19 | class YahooOpenIDService extends Service 20 | { 21 | 22 | protected $name = 'yahoo'; 23 | protected $title = 'Yahoo'; 24 | protected $type = 'OpenID'; 25 | protected $jsArguments = ['popup' => ['width' => 880, 'height' => 520]]; 26 | 27 | protected $url = 'https://me.yahoo.com'; 28 | protected $requiredAttributes = [ 29 | 'name' => ['fullname', 'namePerson'], 30 | // 'login' => ['nickname', 'namePerson/friendly'], 31 | // 'email' => ['email', 'contact/email'], 32 | ]; 33 | protected $optionalAttributes = [ 34 | // 'language' => ['language', 'pref/language'], 35 | // 'gender' => ['gender', 'person/gender'], 36 | // 'timezone' => ['timezone', 'pref/timezone'], 37 | // 'image' => ['image', 'media/image/default'], 38 | ]; 39 | 40 | /*protected function fetchAttributes() { 41 | $this->attributes['fullname'] = $this->attributes['name'].' '.$this->attributes['lastname']; 42 | return true; 43 | }*/ 44 | } -------------------------------------------------------------------------------- /src/assets/js/eauth.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Yii EAuth extension. 3 | * @author Maxim Zemskov 4 | * @link http://github.com/Nodge/yii2-eauth/ 5 | * @license http://www.opensource.org/licenses/bsd-license.php 6 | */ 7 | (function ($) { 8 | var popup, 9 | defaults = { 10 | popup: { 11 | width: 450, 12 | height: 380 13 | } 14 | }; 15 | 16 | function openPopup(options) { 17 | if (popup != null) 18 | popup.close(); 19 | 20 | var redirect_uri, 21 | url = redirect_uri = options.url; 22 | 23 | url += url.indexOf('?') >= 0 ? '&' : '?'; 24 | if (url.indexOf('redirect_uri=') === -1) 25 | url += 'redirect_uri=' + encodeURIComponent(redirect_uri) + '&'; 26 | url += 'js='; 27 | 28 | var centerWidth = (window.screen.width - options.popup.width) / 2, 29 | centerHeight = (window.screen.height - options.popup.height) / 2; 30 | 31 | popup = window.open(url, "yii_eauth_popup", "width=" + options.popup.width + ",height=" + options.popup.height + ",left=" + centerWidth + ",top=" + centerHeight + ",resizable=yes,scrollbars=no,toolbar=no,menubar=no,location=no,directories=no,status=yes"); 32 | popup.focus(); 33 | } 34 | 35 | $.fn.eauth = function (services) { 36 | $(this).on('click', '[data-eauth-service]', function (e) { 37 | e.preventDefault(); 38 | 39 | var service = $(this).data('eauthService'), 40 | options = $.extend({ 41 | url: this.href 42 | }, defaults, services[service]); 43 | 44 | openPopup(options); 45 | }); 46 | }; 47 | })(jQuery); 48 | -------------------------------------------------------------------------------- /src/services/extended/TwitterOAuth1Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services\extended; 11 | 12 | class TwitterOAuth1Service extends \nodge\eauth\services\TwitterOAuth1Service 13 | { 14 | 15 | protected function fetchAttributes() 16 | { 17 | $info = $this->makeSignedRequest('account/verify_credentials.json'); 18 | 19 | $this->attributes['id'] = $info['id']; 20 | $this->attributes['name'] = $info['name']; 21 | $this->attributes['url'] = 'http://twitter.com/account/redirect_by_id?id=' . $info['id_str']; 22 | 23 | $this->attributes['username'] = $info['screen_name']; 24 | $this->attributes['language'] = $info['lang']; 25 | $this->attributes['timezone'] = timezone_name_from_abbr('', $info['utc_offset'], date('I')); 26 | $this->attributes['photo'] = $info['profile_image_url']; 27 | 28 | return true; 29 | } 30 | 31 | /** 32 | * Returns the error array. 33 | * 34 | * @param array $response 35 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 36 | */ 37 | protected function fetchResponseError($response) 38 | { 39 | if (isset($response['errors'])) { 40 | $first = reset($response['errors']); 41 | return [ 42 | 'code' => $first['code'], 43 | 'message' => $first['message'], 44 | ]; 45 | } 46 | return null; 47 | } 48 | } -------------------------------------------------------------------------------- /src/services/extended/OdnoklassnikiOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services\extended; 11 | 12 | class OdnoklassnikiOAuth2Service extends \nodge\eauth\services\OdnoklassnikiOAuth2Service 13 | { 14 | 15 | protected $scopes = [self::SCOPE_VALUABLE_ACCESS]; 16 | 17 | protected function fetchAttributes() 18 | { 19 | parent::fetchAttributes(); 20 | 21 | $info = $this->makeSignedRequest('', [ 22 | 'query' => [ 23 | 'method' => 'users.getInfo', 24 | 'uids' => $this->attributes['id'], 25 | 'fields' => 'url_profile', 26 | 'format' => 'JSON', 27 | 'application_key' => $this->clientPublic, 28 | 'client_id' => $this->clientId, 29 | ], 30 | ]); 31 | 32 | preg_match('/\d+\/{0,1}$/', $info[0]->url_profile, $matches); 33 | $this->attributes['id'] = (int)$matches[0]; 34 | $this->attributes['url'] = $info[0]->url_profile; 35 | 36 | return true; 37 | } 38 | 39 | 40 | /** 41 | * @param string $link 42 | * @param string $message 43 | * @return array 44 | */ 45 | public function wallPost($link, $message) 46 | { 47 | return $this->makeSignedRequest('', [ 48 | 'query' => [ 49 | 'application_key' => $this->clientPublic, 50 | 'method' => 'share.addLink', 51 | 'format' => 'JSON', 52 | 'linkUrl' => $link, 53 | 'comment' => $message, 54 | ], 55 | ]); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/services/YandexOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use OAuth\Common\Token\TokenInterface; 15 | use nodge\eauth\oauth2\Service; 16 | 17 | /** 18 | * Yandex OAuth provider class. 19 | * 20 | * @package application.extensions.eauth.services 21 | */ 22 | class YandexOAuth2Service extends Service 23 | { 24 | 25 | protected $name = 'yandex_oauth'; 26 | protected $title = 'Yandex'; 27 | protected $type = 'OAuth2'; 28 | protected $jsArguments = ['popup' => ['width' => 500, 'height' => 450]]; 29 | protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES; 30 | 31 | protected $scope = []; 32 | protected $providerOptions = [ 33 | 'authorize' => 'https://oauth.yandex.ru/authorize', 34 | 'access_token' => 'https://oauth.yandex.ru/token', 35 | ]; 36 | 37 | protected function fetchAttributes() 38 | { 39 | $info = $this->makeSignedRequest('https://login.yandex.ru/info'); 40 | 41 | $this->attributes['id'] = $info['id']; 42 | $this->attributes['name'] = $info['real_name']; 43 | //$this->attributes['login'] = $info['display_name']; 44 | //$this->attributes['email'] = $info['emails'][0]; 45 | //$this->attributes['email'] = $info['default_email']; 46 | $this->attributes['gender'] = ($info['sex'] == 'male') ? 'M' : 'F'; 47 | 48 | return true; 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/services/extended/VKontakteOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services\extended; 11 | 12 | class VKontakteOAuth2Service extends \nodge\eauth\services\VKontakteOAuth2Service 13 | { 14 | const API_VERSION = '5.57'; 15 | // protected $scope = 'friends'; 16 | 17 | protected function fetchAttributes() 18 | { 19 | $tokenData = $this->getAccessTokenData(); 20 | $info = $this->makeSignedRequest('users.get.json', [ 21 | 'query' => [ 22 | 'uids' => $tokenData['params']['user_id'], 23 | //'fields' => '', // uid, first_name and last_name is always available 24 | 'fields' => 'nickname, sex, bdate, city, country, timezone, photo, photo_medium, photo_big, photo_rec', 25 | 'v' => self::API_VERSION, 26 | ], 27 | ]); 28 | 29 | $info = $info['response'][0]; 30 | 31 | $this->attributes = $info; 32 | $this->attributes['id'] = $info['id']; 33 | $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name']; 34 | $this->attributes['url'] = 'http://vk.com/id' . $info['id']; 35 | 36 | if (!empty($info['nickname'])) { 37 | $this->attributes['username'] = $info['nickname']; 38 | } else { 39 | $this->attributes['username'] = 'id' . $info['id']; 40 | } 41 | 42 | $this->attributes['gender'] = $info['sex'] == 1 ? 'F' : 'M'; 43 | 44 | if (!empty($info['timezone'])) { 45 | $this->attributes['timezone'] = timezone_name_from_abbr('', $info['timezone'] * 3600, date('I')); 46 | } 47 | 48 | return true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/services/extended/FacebookOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services\extended; 11 | 12 | class FacebookOAuth2Service extends \nodge\eauth\services\FacebookOAuth2Service 13 | { 14 | 15 | protected $scopes = [ 16 | self::SCOPE_EMAIL, 17 | self::SCOPE_USER_BIRTHDAY, 18 | self::SCOPE_USER_HOMETOWN, 19 | self::SCOPE_USER_LOCATION, 20 | self::SCOPE_USER_PHOTOS, 21 | ]; 22 | 23 | /** 24 | * http://developers.facebook.com/docs/reference/api/user/ 25 | * 26 | * @see FacebookOAuth2Service::fetchAttributes() 27 | */ 28 | protected function fetchAttributes() 29 | { 30 | $this->attributes = $this->makeSignedRequest('me', [ 31 | 'query' => [ 32 | 'fields' => join(',', [ 33 | 'id', 34 | 'name', 35 | 'link', 36 | 'email', 37 | 'verified', 38 | 'first_name', 39 | 'last_name', 40 | 'gender', 41 | 'birthday', 42 | 'hometown', 43 | 'location', 44 | 'locale', 45 | 'timezone', 46 | 'updated_time', 47 | ]) 48 | ] 49 | ]); 50 | 51 | $this->attributes['photo_url'] = $this->baseApiUrl.$this->getId().'/picture?width=100&height=100'; 52 | 53 | return true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/services/SteamOpenIDService.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\services; 11 | 12 | use nodge\eauth\openid\Service; 13 | 14 | /** 15 | * Steam provider class. 16 | * 17 | * @package application.extensions.eauth.services 18 | */ 19 | class SteamOpenIDService extends Service 20 | { 21 | 22 | protected $name = 'steam'; 23 | protected $title = 'Steam'; 24 | protected $type = 'OpenID'; 25 | protected $jsArguments = ['popup' => ['width' => 990, 'height' => 615]]; 26 | 27 | protected $url = 'http://steamcommunity.com/openid/'; 28 | protected $apiUrl = 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/'; 29 | 30 | public $apiKey; 31 | 32 | protected function fetchAttributes() 33 | { 34 | $chunks = explode('/', $this->attributes['id']); 35 | $id = array_pop($chunks); 36 | 37 | $this->attributes['name'] = $id; 38 | 39 | if ($this->apiKey) { 40 | $response = $this->makeRequest($this->apiUrl, [ 41 | 'query' => [ 42 | 'steamids' => $id, 43 | 'key' => $this->apiKey, 44 | 'format' => 'json' 45 | ] 46 | ]); 47 | 48 | if (isset($response['response']['players'][0])) { 49 | $profile = $response['response']['players'][0]; 50 | 51 | $this->attributes = array_merge($this->attributes, $profile); 52 | $this->attributes['name'] = $profile['personaname']; 53 | $this->attributes['url'] = $profile['profileurl']; 54 | } 55 | } 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/services/TwitterOAuth1Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use OAuth\OAuth1\Token\TokenInterface; 15 | use nodge\eauth\oauth1\Service; 16 | 17 | 18 | /** 19 | * Twitter provider class. 20 | * 21 | * @package application.extensions.eauth.services 22 | */ 23 | class TwitterOAuth1Service extends Service 24 | { 25 | 26 | protected $name = 'twitter'; 27 | protected $title = 'Twitter'; 28 | protected $type = 'OAuth1'; 29 | protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]]; 30 | 31 | protected $providerOptions = [ 32 | 'request' => 'https://api.twitter.com/oauth/request_token', 33 | 'authorize' => 'https://api.twitter.com/oauth/authenticate', //https://api.twitter.com/oauth/authorize 34 | 'access' => 'https://api.twitter.com/oauth/access_token', 35 | ]; 36 | protected $baseApiUrl = 'https://api.twitter.com/1.1/'; 37 | protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES; 38 | 39 | /** 40 | * @return bool 41 | */ 42 | protected function fetchAttributes() 43 | { 44 | $info = $this->makeSignedRequest('account/verify_credentials.json'); 45 | 46 | $this->attributes['id'] = $info['id']; 47 | $this->attributes['name'] = $info['name']; 48 | $this->attributes['url'] = 'http://twitter.com/account/redirect_by_id?id=' . $info['id_str']; 49 | 50 | /*$this->attributes['username'] = $info['screen_name']; 51 | $this->attributes['language'] = $info['lang']; 52 | $this->attributes['timezone'] = timezone_name_from_abbr('', $info['utc_offset'], date('I')); 53 | $this->attributes['photo'] = $info['profile_image_url'];*/ 54 | 55 | return true; 56 | } 57 | 58 | /** 59 | * Authenticate the user. 60 | * 61 | * @return boolean whether user was successfuly authenticated. 62 | */ 63 | public function authenticate() 64 | { 65 | if (isset($_GET['denied'])) { 66 | $this->cancel(); 67 | } 68 | 69 | return parent::authenticate(); 70 | } 71 | } -------------------------------------------------------------------------------- /src/services/LinkedinOAuth1Service.php: -------------------------------------------------------------------------------- 1 | 9 | * @link http://github.com/Nodge/yii2-eauth/ 10 | * @license http://www.opensource.org/licenses/bsd-license.php 11 | */ 12 | 13 | namespace nodge\eauth\services; 14 | 15 | use nodge\eauth\oauth1\Service; 16 | 17 | /** 18 | * LinkedIn provider class. 19 | * 20 | * @package application.extensions.eauth.services 21 | */ 22 | class LinkedinOAuth1Service extends Service 23 | { 24 | 25 | protected $name = 'linkedin'; 26 | protected $title = 'LinkedIn'; 27 | protected $type = 'OAuth1'; 28 | protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]]; 29 | 30 | protected $providerOptions = [ 31 | 'request' => 'https://api.linkedin.com/uas/oauth/requestToken', 32 | 'authorize' => 'https://www.linkedin.com/uas/oauth/authenticate', // https://www.linkedin.com/uas/oauth/authorize 33 | 'access' => 'https://api.linkedin.com/uas/oauth/accessToken', 34 | ]; 35 | protected $baseApiUrl = 'http://api.linkedin.com/v1/'; 36 | 37 | protected function fetchAttributes() 38 | { 39 | $info = $this->makeSignedRequest('people/~:(id,first-name,last-name,public-profile-url)', [ 40 | 'query' => [ 41 | 'format' => 'json', 42 | ], 43 | ]); 44 | 45 | $this->attributes['id'] = $info['id']; 46 | $this->attributes['name'] = $info['firstName'] . ' ' . $info['lastName']; 47 | $this->attributes['url'] = $info['publicProfileUrl']; 48 | 49 | return true; 50 | } 51 | 52 | /** 53 | * Returns the error array. 54 | * 55 | * @param array $response 56 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 57 | */ 58 | protected function fetchResponseError($response) 59 | { 60 | if (isset($response['error-code'])) { 61 | return [ 62 | 'code' => $response['error-code'], 63 | 'message' => $response['message'], 64 | ]; 65 | } else if (isset($response['errorCode'])) { 66 | return [ 67 | 'code' => $response['errorCode'], 68 | 'message' => $response['message'], 69 | ]; 70 | } 71 | return null; 72 | } 73 | } -------------------------------------------------------------------------------- /src/services/LinkedinOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 9 | * @link http://github.com/Nodge/yii2-eauth/ 10 | * @license http://www.opensource.org/licenses/bsd-license.php 11 | */ 12 | 13 | namespace nodge\eauth\services; 14 | 15 | use OAuth\OAuth2\Service\ServiceInterface; 16 | use nodge\eauth\oauth2\Service; 17 | 18 | /** 19 | * LinkedIn provider class. 20 | * 21 | * @package application.extensions.eauth.services 22 | */ 23 | class LinkedinOAuth2Service extends Service 24 | { 25 | 26 | /** 27 | * Defined scopes 28 | * 29 | * @link http://developer.linkedin.com/documents/authentication#granting 30 | */ 31 | const SCOPE_R_BASICPROFILE = 'r_basicprofile'; 32 | const SCOPE_R_FULLPROFILE = 'r_fullprofile'; 33 | const SCOPE_R_EMAILADDRESS = 'r_emailaddress'; 34 | const SCOPE_R_NETWORK = 'r_network'; 35 | const SCOPE_R_CONTACTINFO = 'r_contactinfo'; 36 | const SCOPE_RW_NUS = 'rw_nus'; 37 | const SCOPE_RW_GROUPS = 'rw_groups'; 38 | const SCOPE_W_MESSAGES = 'w_messages'; 39 | 40 | protected $name = 'linkedin_oauth2'; 41 | protected $title = 'LinkedIn'; 42 | protected $type = 'OAuth2'; 43 | protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]]; 44 | 45 | protected $scopes = [self::SCOPE_R_BASICPROFILE]; 46 | protected $providerOptions = [ 47 | 'authorize' => 'https://www.linkedin.com/uas/oauth2/authorization', 48 | 'access_token' => 'https://www.linkedin.com/uas/oauth2/accessToken', 49 | ]; 50 | protected $baseApiUrl = 'https://api.linkedin.com/v1/'; 51 | 52 | protected function fetchAttributes() 53 | { 54 | $info = $this->makeSignedRequest('people/~:(id,first-name,last-name,public-profile-url)', [ 55 | 'query' => [ 56 | 'format' => 'json', 57 | ], 58 | ]); 59 | 60 | $this->attributes['id'] = $info['id']; 61 | $this->attributes['name'] = $info['firstName'] . ' ' . $info['lastName']; 62 | $this->attributes['url'] = $info['publicProfileUrl']; 63 | 64 | return true; 65 | } 66 | 67 | /** 68 | * @return int 69 | */ 70 | public function getAuthorizationMethod() 71 | { 72 | return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING_V2; 73 | } 74 | } -------------------------------------------------------------------------------- /src/assets/css/eauth.css: -------------------------------------------------------------------------------- 1 | 2 | .eauth { 3 | overflow: hidden; 4 | } 5 | 6 | .eauth-list { 7 | margin: -1em 0 0; 8 | padding: 0; 9 | list-style: none; 10 | } 11 | 12 | .eauth-service { 13 | display: inline-block; 14 | vertical-align: top; 15 | margin: 1em 1em 0 0; 16 | } 17 | 18 | .eauth-service-link { 19 | position: relative; 20 | display: block; 21 | width: 60px; 22 | padding-top: 34px; 23 | text-align: center; 24 | } 25 | 26 | .eauth-service-link:before, 27 | .eauth-service-link:after { 28 | content: ''; 29 | position: absolute; 30 | top: 0; 31 | left: 50%; 32 | width: 32px; 33 | height: 32px; 34 | margin-left: -16px; 35 | background: url("../images/auth.png") 0 0 no-repeat; 36 | } 37 | 38 | .eauth-service-link:after { 39 | display: none; 40 | } 41 | 42 | .eauth-service-link:hover:after { 43 | display: block; 44 | } 45 | 46 | /* --- Services --- */ 47 | 48 | .eauth-service-id-google_oauth .eauth-service-link:before { 49 | background-position: 0 -34px; 50 | } 51 | 52 | .eauth-service-id-yandex_oauth .eauth-service-link:before { 53 | background-position: 0 -102px; 54 | } 55 | 56 | .eauth-service-id-twitter .eauth-service-link:before { 57 | background-position: 0 -68px; 58 | } 59 | 60 | .eauth-service-id-vkontakte .eauth-service-link:before { 61 | background-position: 0 -136px; 62 | } 63 | 64 | .eauth-service-id-facebook .eauth-service-link:before { 65 | background-position: 0 -170px; 66 | } 67 | 68 | .eauth-service-id-mailru .eauth-service-link:before { 69 | background-position: 0 -204px; 70 | } 71 | 72 | .eauth-service-id-moikrug .eauth-service-link:before { 73 | background-position: 0 -238px; 74 | } 75 | 76 | .eauth-service-id-odnoklassniki .eauth-service-link:before { 77 | background-position: 0 -272px; 78 | } 79 | 80 | .eauth-service-id-linkedin .eauth-service-link:before, 81 | .eauth-service-id-linkedin_oauth2 .eauth-service-link:before { 82 | background-position: 0 -306px; 83 | } 84 | 85 | .eauth-service-id-github .eauth-service-link:before { 86 | background-position: 0 -340px; 87 | } 88 | 89 | .eauth-service-id-live .eauth-service-link:before { 90 | background-position: 0 -372px; 91 | } 92 | 93 | .eauth-service-id-yahoo .eauth-service-link:before { 94 | background-position: 0 -406px; 95 | } 96 | 97 | .eauth-service-id-steam .eauth-service-link:before { 98 | background-position: 0 -440px; 99 | } 100 | 101 | .eauth-service-id-instagram .eauth-service-link:before { 102 | background-position: 0 -474px; 103 | } -------------------------------------------------------------------------------- /src/services/InstagramOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use nodge\eauth\oauth2\Service; 15 | use OAuth\Common\Token\TokenInterface; 16 | use OAuth\OAuth2\Service\ServiceInterface; 17 | 18 | /** 19 | * Instagram provider class. 20 | * 21 | * @package application.extensions.eauth.services 22 | */ 23 | class InstagramOAuth2Service extends Service 24 | { 25 | 26 | /** 27 | * Defined scopes 28 | * @link https://instagram.com/developer/authentication/ 29 | */ 30 | const SCOPE_BASIC = 'basic'; 31 | const SCOPE_COMMENTS = 'comments'; 32 | const SCOPE_RELATIONSHIPS = 'relationships'; 33 | const SCOPE_LIKES = 'likes'; 34 | 35 | protected $name = 'instagram'; 36 | protected $title = 'Instagram'; 37 | protected $type = 'OAuth2'; 38 | protected $jsArguments = ['popup' => ['width' => 900, 'height' => 550]]; 39 | protected $popupDisplayName = false; 40 | 41 | protected $scopes = [self::SCOPE_BASIC]; 42 | protected $providerOptions = [ 43 | 'authorize' => 'https://api.instagram.com/oauth/authorize/', 44 | 'access_token' => 'https://api.instagram.com/oauth/access_token', 45 | ]; 46 | protected $baseApiUrl = 'https://api.instagram.com/v1/'; 47 | 48 | protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES; 49 | 50 | protected function fetchAttributes() 51 | { 52 | $info = $this->makeSignedRequest('users/self'); 53 | $data = $info['data']; 54 | 55 | $this->attributes = array_merge($this->attributes, [ 56 | 'id' => $data['id'], 57 | 'username' => $data['username'], 58 | 'full_name' => $data['full_name'], 59 | 'profile_picture' => $data['profile_picture'], 60 | 'bio' => $data['bio'], 61 | 'website' => $data['website'], 62 | 'counts' => $data['counts'] 63 | ]); 64 | 65 | return true; 66 | } 67 | 68 | /** 69 | * @return int 70 | */ 71 | public function getAuthorizationMethod() 72 | { 73 | return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING; 74 | } 75 | 76 | /** 77 | * Returns the protected resource. 78 | * 79 | * @param string $url url to request. 80 | * @param array $options HTTP request options. Keys: query, data, referer. 81 | * @param boolean $parseResponse Whether to parse response. 82 | * @return mixed the response. 83 | */ 84 | public function makeSignedRequest($url, $options = [], $parseResponse = true) 85 | { 86 | $options['query']['format'] = 'json'; 87 | return parent::makeSignedRequest($url, $options, $parseResponse); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/services/MailruOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use nodge\eauth\oauth2\Service; 15 | 16 | /** 17 | * Mail.Ru provider class. 18 | * 19 | * @package application.extensions.eauth.services 20 | */ 21 | class MailruOAuth2Service extends Service 22 | { 23 | 24 | protected $name = 'mailru'; 25 | protected $title = 'Mail.ru'; 26 | protected $type = 'OAuth2'; 27 | protected $jsArguments = ['popup' => ['width' => 580, 'height' => 400]]; 28 | 29 | protected $scopes = []; 30 | protected $providerOptions = [ 31 | 'authorize' => 'https://connect.mail.ru/oauth/authorize', 32 | 'access_token' => 'https://connect.mail.ru/oauth/token', 33 | ]; 34 | protected $baseApiUrl = 'http://www.appsmail.ru/platform/api'; 35 | 36 | protected function fetchAttributes() 37 | { 38 | $tokenData = $this->getAccessTokenData(); 39 | 40 | $info = $this->makeSignedRequest('/', [ 41 | 'query' => [ 42 | 'uids' => $tokenData['params']['x_mailru_vid'], 43 | 'method' => 'users.getInfo', 44 | 'app_id' => $this->clientId, 45 | ], 46 | ]); 47 | 48 | $info = $info[0]; 49 | 50 | $this->attributes['id'] = $info['uid']; 51 | $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name']; 52 | $this->attributes['url'] = $info['link']; 53 | 54 | return true; 55 | } 56 | 57 | /** 58 | * Returns the protected resource. 59 | * 60 | * @param string $url url to request. 61 | * @param array $options HTTP request options. Keys: query, data, referer. 62 | * @param boolean $parseResponse Whether to parse response. 63 | * @return mixed the response. 64 | */ 65 | public function makeSignedRequest($url, $options = [], $parseResponse = true) 66 | { 67 | $token = $this->getAccessTokenData(); 68 | if (isset($token)) { 69 | $options['query']['secure'] = 1; 70 | $options['query']['session_key'] = $token['access_token']; 71 | $params = ''; 72 | ksort($options['query']); 73 | foreach ($options['query'] as $k => $v) { 74 | $params .= $k . '=' . $v; 75 | } 76 | $options['query']['sig'] = md5($params . $this->clientSecret); 77 | } 78 | return parent::makeSignedRequest($url, $options, $parseResponse); 79 | } 80 | 81 | /** 82 | * Returns the error array. 83 | * 84 | * @param array $response 85 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 86 | */ 87 | protected function fetchResponseError($response) 88 | { 89 | if (isset($response['error'])) { 90 | return [ 91 | 'code' => $response['error']['error_code'], 92 | 'message' => $response['error']['error_msg'], 93 | ]; 94 | } else { 95 | return null; 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /src/services/LiveOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use OAuth\OAuth2\Service\ServiceInterface; 15 | use nodge\eauth\oauth2\Service; 16 | 17 | /** 18 | * Microsoft Live provider class. 19 | * 20 | * @package application.extensions.eauth.services 21 | */ 22 | class LiveOAuth2Service extends Service 23 | { 24 | 25 | const SCOPE_BASIC = 'wl.basic'; 26 | const SCOPE_OFFLINE = 'wl.offline_access'; 27 | const SCOPE_SIGNIN = 'wl.signin'; 28 | const SCOPE_BIRTHDAY = 'wl.birthday'; 29 | const SCOPE_CALENDARS = 'wl.calendars'; 30 | const SCOPE_CALENDARS_UPDATE = 'wl.calendars_update'; 31 | const SCOPE_CONTACTS_BIRTHDAY = 'wl.contacts_birthday'; 32 | const SCOPE_CONTACTS_CREATE = 'wl.contacts_create'; 33 | const SCOPE_CONTACTS_CALENDARS = 'wl.contacts_calendars'; 34 | const SCOPE_CONTACTS_PHOTOS = 'wl.contacts_photos'; 35 | const SCOPE_CONTACTS_SKYDRIVE = 'wl.contacts_skydrive'; 36 | const SCOPE_EMAILS = 'wl.emails'; 37 | const sCOPE_EVENTS_CREATE = 'wl.events_create'; 38 | const SCOPE_MESSENGER = 'wl.messenger'; 39 | const SCOPE_PHONE_NUMBERS = 'wl.phone_numbers'; 40 | const SCOPE_PHOTOS = 'wl.photos'; 41 | const SCOPE_POSTAL_ADDRESSES = 'wl.postal_addresses'; 42 | const SCOPE_SHARE = 'wl.share'; 43 | const SCOPE_SKYDRIVE = 'wl.skydrive'; 44 | const SCOPE_SKYDRIVE_UPDATE = 'wl.skydrive_update'; 45 | const SCOPE_WORK_PROFILE = 'wl.work_profile'; 46 | const SCOPE_APPLICATIONS = 'wl.applications'; 47 | const SCOPE_APPLICATIONS_CREATE = 'wl.applications_create'; 48 | 49 | protected $name = 'live'; 50 | protected $title = 'Live'; 51 | protected $type = 'OAuth2'; 52 | protected $jsArguments = ['popup' => ['width' => 500, 'height' => 600]]; 53 | 54 | protected $scopes = [self::SCOPE_BASIC]; 55 | protected $providerOptions = [ 56 | 'authorize' => 'https://login.live.com/oauth20_authorize.srf', 57 | 'access_token' => 'https://login.live.com/oauth20_token.srf', 58 | ]; 59 | protected $baseApiUrl = 'https://apis.live.net/v5.0/'; 60 | 61 | protected function fetchAttributes() 62 | { 63 | $info = $this->makeSignedRequest('me'); 64 | 65 | $this->attributes['id'] = $info['id']; 66 | $this->attributes['name'] = $info['name']; 67 | $this->attributes['url'] = 'https://profile.live.com/cid-' . $info['id'] . '/'; 68 | 69 | /*$this->attributes['email'] = $info['emails']['account']; 70 | $this->attributes['first_name'] = $info['first_name']; 71 | $this->attributes['last_name'] = $info['last_name']; 72 | $this->attributes['gender'] = $info['gender']; 73 | $this->attributes['locale'] = $info['locale'];*/ 74 | 75 | return true; 76 | } 77 | 78 | /** 79 | * @return int 80 | */ 81 | public function getAuthorizationMethod() 82 | { 83 | return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING; 84 | } 85 | } -------------------------------------------------------------------------------- /src/services/GitHubOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use OAuth\Common\Token\TokenInterface; 15 | use OAuth\OAuth2\Service\ServiceInterface; 16 | use nodge\eauth\oauth2\Service; 17 | 18 | /** 19 | * GitHub provider class. 20 | * 21 | * @package application.extensions.eauth.services 22 | */ 23 | class GitHubOAuth2Service extends Service 24 | { 25 | 26 | /** 27 | * Defined scopes, see http://developer.github.com/v3/oauth/ for definitions 28 | */ 29 | const SCOPE_USER = 'user'; 30 | const SCOPE_PUBLIC_REPO = 'public_repo'; 31 | const SCOPE_REPO = 'repo'; 32 | const SCOPE_DELETE_REPO = 'delete_repo'; 33 | const SCOPE_GIST = 'gist'; 34 | 35 | protected $name = 'github'; 36 | protected $title = 'GitHub'; 37 | protected $type = 'OAuth2'; 38 | protected $jsArguments = ['popup' => ['width' => 600, 'height' => 450]]; 39 | 40 | protected $scopes = []; 41 | protected $providerOptions = [ 42 | 'authorize' => 'https://github.com/login/oauth/authorize', 43 | 'access_token' => 'https://github.com/login/oauth/access_token', 44 | ]; 45 | protected $baseApiUrl = 'https://api.github.com/'; 46 | 47 | protected $tokenDefaultLifetime = TokenInterface::EOL_NEVER_EXPIRES; 48 | protected $errorAccessDeniedCode = 'user_denied'; 49 | 50 | protected function fetchAttributes() 51 | { 52 | $info = $this->makeSignedRequest('user'); 53 | 54 | $this->attributes['id'] = $info['id']; 55 | $this->attributes['name'] = $info['login']; 56 | $this->attributes['url'] = $info['html_url']; 57 | 58 | return true; 59 | } 60 | 61 | /** 62 | * Used to configure response type -- we want JSON from github, default is query string format 63 | * 64 | * @return array 65 | */ 66 | public function getExtraOAuthHeaders() 67 | { 68 | return ['Accept' => 'application/json']; 69 | } 70 | 71 | /** 72 | * Required for GitHub API calls. 73 | * 74 | * @return array 75 | */ 76 | public function getExtraApiHeaders() 77 | { 78 | return ['Accept' => 'application/vnd.github.beta+json']; 79 | } 80 | 81 | /** 82 | * @return int 83 | */ 84 | public function getAuthorizationMethod() 85 | { 86 | return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING; 87 | } 88 | 89 | /** 90 | * Returns the error array. 91 | * 92 | * @param array $response 93 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 94 | */ 95 | protected function fetchResponseError($response) 96 | { 97 | if (isset($response['message'])) { 98 | return [ 99 | 'code' => isset($response['error']) ? $response['code'] : 0, 100 | 'message' => $response['message'], 101 | ]; 102 | } else { 103 | return null; 104 | } 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /src/Widget.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | use Yii; 13 | 14 | /** 15 | * The EAuthWidget widget prints buttons to authenticate user with OpenID and OAuth providers. 16 | * 17 | * @package application.extensions.eauth 18 | */ 19 | class Widget extends \yii\base\Widget 20 | { 21 | 22 | /** 23 | * @var string EAuth component name. 24 | */ 25 | public $component = 'eauth'; 26 | 27 | /** 28 | * @var array the services. 29 | * @see EAuth::getServices() 30 | */ 31 | public $services = null; 32 | 33 | /** 34 | * @var array predefined services. If null then use all services. Default is null. 35 | */ 36 | public $predefinedServices = null; 37 | 38 | /** 39 | * @var boolean whether to use popup window for authorization dialog. Javascript required. 40 | */ 41 | public $popup = null; 42 | 43 | /** 44 | * @var string the action to use for dialog destination. Default: the current route. 45 | */ 46 | public $action = null; 47 | 48 | /** 49 | * @var boolean include the CSS file. Default is true. 50 | * If this is set false, you are responsible to explicitly include the necessary CSS file in your page. 51 | */ 52 | public $assetBundle = 'nodge\\eauth\\assets\\WidgetAssetBundle'; 53 | 54 | /** 55 | * Initializes the widget. 56 | * This method is called by {@link CBaseController::createWidget} 57 | * and {@link CBaseController::beginWidget} after the widget's 58 | * properties have been initialized. 59 | */ 60 | public function init() 61 | { 62 | parent::init(); 63 | 64 | // EAuth component 65 | /** @var $component \nodge\eauth\EAuth */ 66 | $component = Yii::$app->get($this->component); 67 | 68 | // Some default properties from component configuration 69 | if (!isset($this->services)) { 70 | $this->services = $component->getServices(); 71 | } 72 | 73 | if (is_array($this->predefinedServices)) { 74 | $_services = []; 75 | foreach ($this->predefinedServices as $_serviceName) { 76 | if (isset($this->services[$_serviceName])) { 77 | $_services[$_serviceName] = $this->services[$_serviceName]; 78 | } 79 | } 80 | $this->services = $_services; 81 | } 82 | 83 | if (!isset($this->popup)) { 84 | $this->popup = $component->popup; 85 | } 86 | 87 | // Set the current route, if it is not set. 88 | if (!isset($this->action)) { 89 | $this->action = '/' . Yii::$app->requestedRoute; 90 | } 91 | } 92 | 93 | /** 94 | * Executes the widget. 95 | * This method is called by {@link CBaseController::endWidget}. 96 | */ 97 | public function run() 98 | { 99 | parent::run(); 100 | echo $this->render('widget', [ 101 | 'id' => $this->getId(), 102 | 'services' => $this->services, 103 | 'action' => $this->action, 104 | 'popup' => $this->popup, 105 | 'assetBundle' => $this->assetBundle, 106 | ]); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/services/FacebookOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use nodge\eauth\oauth2\Service; 15 | 16 | /** 17 | * Facebook provider class. 18 | * 19 | * @package application.extensions.eauth.services 20 | */ 21 | class FacebookOAuth2Service extends Service 22 | { 23 | 24 | /** 25 | * Full list of scopes may be found here: 26 | * https://developers.facebook.com/docs/authentication/permissions/ 27 | */ 28 | const SCOPE_EMAIL = 'email'; 29 | const SCOPE_USER_BIRTHDAY = 'user_birthday'; 30 | const SCOPE_USER_HOMETOWN = 'user_hometown'; 31 | const SCOPE_USER_LOCATION = 'user_location'; 32 | const SCOPE_USER_PHOTOS = 'user_photos'; 33 | 34 | protected $name = 'facebook'; 35 | protected $title = 'Facebook'; 36 | protected $type = 'OAuth2'; 37 | protected $jsArguments = ['popup' => ['width' => 585, 'height' => 290]]; 38 | 39 | protected $scopes = []; 40 | protected $providerOptions = [ 41 | 'authorize' => 'https://www.facebook.com/dialog/oauth', 42 | 'access_token' => 'https://graph.facebook.com/oauth/access_token', 43 | ]; 44 | protected $baseApiUrl = 'https://graph.facebook.com/v2.8/'; 45 | 46 | protected function fetchAttributes() 47 | { 48 | $info = $this->makeSignedRequest('me', [ 49 | 'query' => [ 50 | 'fields' => join(',', [ 51 | 'id', 52 | 'name', 53 | 'link' 54 | ]) 55 | ] 56 | ]); 57 | 58 | $this->attributes['id'] = $info['id']; 59 | $this->attributes['name'] = $info['name']; 60 | $this->attributes['url'] = $info['link']; 61 | 62 | return true; 63 | } 64 | 65 | /** 66 | * @param string $response 67 | * @return array 68 | */ 69 | public function parseAccessTokenResponse($response) 70 | { 71 | // Facebook gives us a query string or json 72 | if ($response[0] === '{') { 73 | return json_decode($response, true); 74 | } 75 | else { 76 | parse_str($response, $data); 77 | return $data; 78 | } 79 | } 80 | 81 | /** 82 | * Returns the error array. 83 | * 84 | * @param array $response 85 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 86 | */ 87 | protected function fetchResponseError($response) 88 | { 89 | if (isset($response['error'])) { 90 | return [ 91 | 'code' => $response['error']['code'], 92 | 'message' => $response['error']['message'], 93 | ]; 94 | } else { 95 | return null; 96 | } 97 | } 98 | 99 | /** 100 | * @param array $data 101 | * @return string|null 102 | */ 103 | public function getAccessTokenResponseError($data) 104 | { 105 | $error = $this->fetchResponseError($data); 106 | if (!$error) { 107 | return null; 108 | } 109 | return $error['code'].': '.$error['message']; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/IAuthService.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | /** 13 | * IAuthService is the interface for all service types and providers. 14 | * 15 | * @package application.extensions.eauth 16 | */ 17 | interface IAuthService 18 | { 19 | 20 | /** 21 | * Returns service name(id). 22 | */ 23 | public function getServiceName(); 24 | 25 | /** 26 | * Returns service title. 27 | */ 28 | public function getServiceTitle(); 29 | 30 | /** 31 | * Returns service type (e.g. OpenID, OAuth). 32 | */ 33 | public function getServiceType(); 34 | 35 | /** 36 | * Returns arguments for the jQuery.eauth() javascript function. 37 | */ 38 | public function getJsArguments(); 39 | 40 | 41 | /** 42 | * Sets {@link EAuth} application component 43 | * 44 | * @param EAuth $component the application auth component. 45 | */ 46 | public function setComponent($component); 47 | 48 | /** 49 | * Returns the {@link EAuth} application component. 50 | */ 51 | public function getComponent(); 52 | 53 | 54 | /** 55 | * Sets redirect url after successful authorization. 56 | * 57 | * @param string $url url to redirect. 58 | */ 59 | public function setRedirectUrl($url); 60 | 61 | /** 62 | * Returns the redirect url after successful authorization. 63 | */ 64 | public function getRedirectUrl(); 65 | 66 | 67 | /** 68 | * Sets redirect url after unsuccessful authorization (e.g. user canceled). 69 | * 70 | * @param string $url url to redirect. 71 | */ 72 | public function setCancelUrl($url); 73 | 74 | /** 75 | * Returns the redirect url after unsuccessful authorization (e.g. user canceled). 76 | */ 77 | public function getCancelUrl(); 78 | 79 | 80 | /** 81 | * Authenticate the user. 82 | */ 83 | public function authenticate(); 84 | 85 | /** 86 | * Whether user was successfuly authenticated. 87 | */ 88 | public function getIsAuthenticated(); 89 | 90 | 91 | /** 92 | * Redirect to the url. If url is null, {@link redirectUrl} will be used. 93 | * 94 | * @param string $url url to redirect. 95 | */ 96 | public function redirect($url = null); 97 | 98 | /** 99 | * Redirect to the {@link cancelUrl} or simply close the popup window. 100 | */ 101 | public function cancel(); 102 | 103 | 104 | /** 105 | * Returns the user unique id. 106 | */ 107 | public function getId(); 108 | 109 | /** 110 | * Returns the array that contains all available authorization attributes. 111 | */ 112 | public function getAttributes(); 113 | 114 | /** 115 | * Returns the authorization attribute value. 116 | * 117 | * @param string $key the attribute name. 118 | * @param mixed $default the default value. 119 | */ 120 | public function getAttribute($key, $default = null); 121 | 122 | /** 123 | * Whether the authorization attribute exists. 124 | * 125 | * @param string $key the attribute name. 126 | */ 127 | public function hasAttribute($key); 128 | 129 | } -------------------------------------------------------------------------------- /src/services/OdnoklassnikiOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 10 | * @link http://github.com/Nodge/yii2-eauth/ 11 | * @license http://www.opensource.org/licenses/bsd-license.php 12 | */ 13 | 14 | namespace nodge\eauth\services; 15 | 16 | use nodge\eauth\oauth2\Service; 17 | 18 | /** 19 | * Odnoklassniki.Ru provider class. 20 | * 21 | * @package application.extensions.eauth.services 22 | */ 23 | class OdnoklassnikiOAuth2Service extends Service 24 | { 25 | 26 | const SCOPE_VALUABLE_ACCESS = 'VALUABLE ACCESS'; 27 | const SCOPE_SET_STATUS = 'SET STATUS'; 28 | const SCOPE_PHOTO_CONTENT = 'PHOTO CONTENT'; 29 | 30 | protected $name = 'odnoklassniki'; 31 | protected $title = 'Odnoklassniki'; 32 | protected $type = 'OAuth2'; 33 | protected $jsArguments = ['popup' => ['width' => 680, 'height' => 500]]; 34 | 35 | protected $clientPublic; 36 | protected $scopes = []; 37 | protected $scopeSeparator = ';'; 38 | protected $providerOptions = [ 39 | 'authorize' => 'https://connect.ok.ru/oauth/authorize', 40 | 'access_token' => 'https://api.ok.ru/oauth/token.do', 41 | ]; 42 | protected $baseApiUrl = 'https://api.ok.ru/api/'; 43 | 44 | protected $tokenDefaultLifetime = 1500; // about 25 minutes 45 | protected $validateState = false; 46 | 47 | protected function fetchAttributes() 48 | { 49 | $info = $this->makeSignedRequest('', [ 50 | 'query' => [ 51 | 'method' => 'users.getCurrentUser', 52 | 'format' => 'JSON', 53 | 'application_key' => $this->clientPublic, 54 | 'client_id' => $this->clientId, 55 | ], 56 | ]); 57 | 58 | $this->attributes['id'] = $info['uid']; 59 | $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name']; 60 | 61 | return true; 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function getClientPublic() 68 | { 69 | return $this->clientPublic; 70 | } 71 | 72 | /** 73 | * @param string $clientPublic 74 | */ 75 | public function setClientPublic($clientPublic) 76 | { 77 | $this->clientPublic = $clientPublic; 78 | } 79 | 80 | /** 81 | * Returns the protected resource. 82 | * 83 | * @param string $url url to request. 84 | * @param array $options HTTP request options. Keys: query, data, referer. 85 | * @param boolean $parseResponse Whether to parse response. 86 | * @return mixed the response. 87 | */ 88 | public function makeSignedRequest($url, $options = [], $parseResponse = true) 89 | { 90 | $token = $this->getAccessTokenData(); 91 | if (isset($token)) { 92 | $params = ''; 93 | ksort($options['query']); 94 | foreach ($options['query'] as $k => $v) { 95 | $params .= $k . '=' . $v; 96 | } 97 | $options['query']['sig'] = md5($params . md5($token['access_token'] . $this->clientSecret)); 98 | $options['query']['access_token'] = $token['access_token']; 99 | } 100 | return parent::makeSignedRequest($url, $options, $parseResponse); 101 | } 102 | 103 | /** 104 | * Returns the error array. 105 | * 106 | * @param array $response 107 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 108 | */ 109 | protected function fetchResponseError($response) 110 | { 111 | if (isset($response['error_code'])) { 112 | return [ 113 | 'code' => $response['error_code'], 114 | 'message' => $response['error_msg'], 115 | ]; 116 | } else { 117 | return null; 118 | } 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/services/VKontakteOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use nodge\eauth\oauth2\Service; 15 | use OAuth\OAuth2\Service\ServiceInterface; 16 | 17 | /** 18 | * VKontakte provider class. 19 | * 20 | * @package application.extensions.eauth.services 21 | */ 22 | class VKontakteOAuth2Service extends Service 23 | { 24 | 25 | const SCOPE_FRIENDS = 'friends'; 26 | const API_VERSION = '5.57'; 27 | 28 | protected $name = 'vkontakte'; 29 | protected $title = 'VK.com'; 30 | protected $type = 'OAuth2'; 31 | protected $jsArguments = ['popup' => ['width' => 585, 'height' => 350]]; 32 | 33 | protected $scopes = [self::SCOPE_FRIENDS]; 34 | protected $providerOptions = [ 35 | 'authorize' => 'http://api.vk.com/oauth/authorize', 36 | 'access_token' => 'https://api.vk.com/oauth/access_token', 37 | ]; 38 | protected $baseApiUrl = 'https://api.vk.com/method/'; 39 | 40 | protected function fetchAttributes() 41 | { 42 | $tokenData = $this->getAccessTokenData(); 43 | $info = $this->makeSignedRequest('users.get.json', [ 44 | 'query' => [ 45 | 'uids' => $tokenData['params']['user_id'], 46 | 'fields' => '', // uid, first_name and last_name is always available 47 | //'fields' => 'nickname, sex, bdate, city, country, timezone, photo, photo_medium, photo_big, photo_rec', 48 | 'v' => self::API_VERSION, 49 | ], 50 | ]); 51 | 52 | $info = $info['response'][0]; 53 | 54 | $this->attributes['id'] = $info['id']; 55 | $this->attributes['name'] = $info['first_name'] . ' ' . $info['last_name']; 56 | $this->attributes['url'] = 'http://vk.com/id' . $info['id']; 57 | 58 | /*if (!empty($info['nickname'])) 59 | $this->attributes['username'] = $info['nickname']; 60 | else 61 | $this->attributes['username'] = 'id'.$info['uid']; 62 | 63 | $this->attributes['gender'] = $info['sex'] == 1 ? 'F' : 'M'; 64 | 65 | $this->attributes['city'] = $info['city']; 66 | $this->attributes['country'] = $info['country']; 67 | 68 | $this->attributes['timezone'] = timezone_name_from_abbr('', $info['timezone']*3600, date('I'));; 69 | 70 | $this->attributes['photo'] = $info['photo']; 71 | $this->attributes['photo_medium'] = $info['photo_medium']; 72 | $this->attributes['photo_big'] = $info['photo_big']; 73 | $this->attributes['photo_rec'] = $info['photo_rec'];*/ 74 | 75 | return true; 76 | } 77 | 78 | /** 79 | * Returns the error array. 80 | * 81 | * @param array $response 82 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 83 | */ 84 | protected function fetchResponseError($response) 85 | { 86 | if (isset($response['error'])) { 87 | return [ 88 | 'code' => is_string($response['error']) ? 0 : $response['error']['error_code'], 89 | // 'message' => is_string($response['error']) ? $response['error'] : $response['error']['error_msg'], 90 | // 'message' => is_string($response['error']) ? $response['error'] : $response['error']['error_msg'], 91 | ]; 92 | } else { 93 | return null; 94 | } 95 | } 96 | 97 | /** 98 | * @param array $data 99 | * @return string|null 100 | */ 101 | public function getAccessTokenResponseError($data) 102 | { 103 | if (!isset($data['error'])) { 104 | return null; 105 | } 106 | $error = $data['error']; 107 | if (isset($data['error_description'])) { 108 | $error .= ': ' . $data['error_description']; 109 | } 110 | return $error; 111 | } 112 | 113 | /** 114 | * Returns a class constant from ServiceInterface defining the authorization method used for the API. 115 | * 116 | * @return int 117 | */ 118 | public function getAuthorizationMethod() 119 | { 120 | return ServiceInterface::AUTHORIZATION_METHOD_QUERY_STRING; 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /src/oauth1/Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth1; 11 | 12 | use Yii; 13 | use OAuth\Common\Exception\Exception as OAuthException; 14 | use OAuth\Common\Http\Uri\Uri; 15 | use OAuth\Common\Consumer\Credentials; 16 | use OAuth\OAuth1\Signature\Signature; 17 | use nodge\eauth\EAuth; 18 | use nodge\eauth\ErrorException; 19 | use nodge\eauth\IAuthService; 20 | use nodge\eauth\oauth\ServiceBase; 21 | 22 | /** 23 | * EOAuthService is a base class for all OAuth providers. 24 | * 25 | * @package application.extensions.eauth 26 | */ 27 | abstract class Service extends ServiceBase implements IAuthService 28 | { 29 | 30 | /** 31 | * @var string OAuth2 client id. 32 | */ 33 | protected $key; 34 | 35 | /** 36 | * @var string OAuth2 client secret key. 37 | */ 38 | protected $secret; 39 | 40 | /** 41 | * @var array Provider options. Must contain the keys: request, authorize, access. 42 | */ 43 | protected $providerOptions = [ 44 | 'request' => '', 45 | 'authorize' => '', 46 | 'access' => '', 47 | ]; 48 | 49 | /** 50 | * @var ServiceProxy 51 | */ 52 | private $_proxy; 53 | 54 | /** 55 | * Initialize the component. 56 | * 57 | * @param EAuth $component the component instance. 58 | * @param array $options properties initialization. 59 | */ 60 | // public function init($component, $options = []) { 61 | // parent::init($component, $options); 62 | // } 63 | 64 | /** 65 | * @param string $key 66 | */ 67 | public function setKey($key) 68 | { 69 | $this->key = $key; 70 | } 71 | 72 | /** 73 | * @param string $secret 74 | */ 75 | public function setSecret($secret) 76 | { 77 | $this->secret = $secret; 78 | } 79 | 80 | /** 81 | * @return ServiceProxy 82 | */ 83 | protected function getProxy() 84 | { 85 | if (!isset($this->_proxy)) { 86 | $storage = $this->getTokenStorage(); 87 | $httpClient = $this->getHttpClient(); 88 | $credentials = new Credentials($this->key, $this->secret, $this->getCallbackUrl()); 89 | $signature = new Signature($credentials); 90 | $this->_proxy = new ServiceProxy($credentials, $httpClient, $storage, $signature, null, $this); 91 | } 92 | return $this->_proxy; 93 | } 94 | 95 | /** 96 | * Authenticate the user. 97 | * 98 | * @return boolean whether user was successfuly authenticated. 99 | * @throws ErrorException 100 | */ 101 | public function authenticate() 102 | { 103 | try { 104 | $proxy = $this->getProxy(); 105 | 106 | if (!empty($_GET['oauth_token'])) { 107 | $token = $proxy->retrieveAccessToken(); 108 | 109 | // This was a callback request, get the token now 110 | $proxy->requestAccessToken($_GET['oauth_token'], $_GET['oauth_verifier'], $token->getRequestTokenSecret()); 111 | 112 | $this->authenticated = true; 113 | } else if ($proxy->hasValidAccessToken()) { 114 | $this->authenticated = true; 115 | } else { 116 | // extra request needed for oauth1 to request a request token :-) 117 | $token = $proxy->requestRequestToken(); 118 | /** @var $url Uri */ 119 | $url = $proxy->getAuthorizationUri(['oauth_token' => $token->getRequestToken()]); 120 | Yii::$app->getResponse()->redirect($url->getAbsoluteUri())->send(); 121 | } 122 | } catch (OAuthException $e) { 123 | throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e); 124 | } 125 | 126 | return $this->getIsAuthenticated(); 127 | } 128 | 129 | /** 130 | * @return string 131 | */ 132 | public function getRequestTokenEndpoint() 133 | { 134 | return $this->providerOptions['request']; 135 | } 136 | 137 | /** 138 | * @return string 139 | */ 140 | public function getAuthorizationEndpoint() 141 | { 142 | return $this->providerOptions['authorize']; 143 | } 144 | 145 | /** 146 | * @return string 147 | */ 148 | public function getAccessTokenEndpoint() 149 | { 150 | return $this->providerOptions['access']; 151 | } 152 | 153 | /** 154 | * @return array 155 | */ 156 | public function getAccessTokenArgumentNames() 157 | { 158 | return [ 159 | 'oauth_token' => 'oauth_token', 160 | 'oauth_token_secret' => 'oauth_token_secret', 161 | 'oauth_expires_in' => 'oauth_expires_in', 162 | ]; 163 | } 164 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Yii2 EAuth Change Log 2 | ===================== 3 | 4 | ### 2.5.0 (10.04.2017) 5 | * Fix bug with access token expire time in Facebook service (#102) 6 | * Use the latest Graph Api v2.8 for Facebook 7 | * Using version when making VK API call (#92) 8 | * New authorization and API url for Odnoklassniki service (#73) 9 | * Added Polish translation (#93) 10 | * Added Romanian translation (#94) 11 | 12 | ### 2.4.1 (13.01.2016) 13 | * Move response parsing from oauth to base service (#71) 14 | * Get user profile from Steam API (#70) 15 | 16 | ### 2.4.0 (03.01.2016) 17 | * Fixed error param names for Facebook (#63) 18 | * Use the latest Graph Api v2.5 for Facebook (#65) 19 | * Fixed `makeRequest` method (#68) 20 | * Added `makeRequest` method to OpenID services 21 | 22 | ### 2.3.0 (17.10.2015) 23 | * Added InstagramOAuth2Service (#61) 24 | * Fixed default token lifetime (#53) 25 | * Replace array() with [] (#54) 26 | * Remove deprecated Google OpenID service (#56) 27 | * Remove deprecated Yandex OpenID service 28 | 29 | ### 2.2.4 (27.07.2015) 30 | * Fixed typo in `oauth2/Service.php` (#34) 31 | * Added German translation 32 | * Added `email` attribute to `LinkedinOAuth2Service.php` 33 | 34 | ### 2.2.3 (15.07.2014) 35 | * Added ability to call public api methods (without access token) (#28) 36 | 37 | ### 2.2.2 (15.07.2014) 38 | * Fixed wrong redirect_uri when popup is used 39 | 40 | ### 2.2.1 (25.04.2014) 41 | * Fix missing query params in callback urls (#26) 42 | * Follow Yii2 code style 43 | 44 | ### 2.2.0 (19.04.2014) 45 | * Support for PHPoAuthLib v0.3 (#22) 46 | * Support for Yii2 beta 47 | * Internal state implementation replaced to PHPoAuthLib storage 48 | 49 | ### 2.1.5 (24.03.2014) 50 | * Fixed Yii2 refactor (#17) 51 | * PSR-4 52 | 53 | ### 2.1.4 (11.03.2014) 54 | * Fixed wrong callbackUrl in oauth\ServiceBase when UrlManager uses prettyUrl=false and showScript=false (#12) 55 | * Fixed Yii::t() calls according to Yii2 i18n Named Placeholders (#14) 56 | * Fixed Yii2 refactor #2630 (#15) 57 | 58 | ### 2.1.3 (30.01.2014) 59 | * Yii2 update (Request methods has been refactored). 60 | 61 | ### 2.1.2 (17.01.2014) 62 | * Fixed typo in oauth2\ServiceProxy 63 | 64 | ### 2.1.1 (07.01.2014) 65 | * Fixed scope validation for OAuth services. 66 | 67 | ### 2.1.0 (22.12.2013) 68 | * Reorganize project with new namespace. 69 | * Assets bundle has been moved. 70 | * Fixed typo in HttpClient (#8). 71 | * Added default User-Agent header to HttpClient. 72 | * Disabled CSRF validation for OpenID callbacks. 73 | * Optimized icons file. 74 | * Added SteamOpenIDService. 75 | * Improved redirect widget. 76 | 77 | ### 2.0.3 (26.10.2013) 78 | * Fixed redirect_uri when not using url rule (#2). 79 | * Fixed hasValidAccessToken() method for OAuth1 services (#3). 80 | * Fixed auto login cookie (#4). 81 | 82 | ### 2.0.2 (12.10.2013) 83 | * Fixed ServiceProxy constructor to match its interface (#1). 84 | * Added HttpClient with logging support and curl/streams fallback. 85 | * TokenStorage and HttpClient are configurable now. 86 | 87 | ### 2.0.1 (08.09.2013) 88 | * Fixed package versions in the composer.json. 89 | * Fixed directories names. 90 | * Added support for custom scope separator in OAuth2 services. 91 | * Added support for additional headers for OAuth2 requests. 92 | * Added method to get error from access token response. 93 | * Added GitHubOAuth2Service. 94 | * Added LinkedinOAuth2Service. 95 | * Added MailruOAuth2Service. 96 | * Added OdnoklassnikiOAuth2Service. 97 | * Added LiveOAuth2Service. 98 | * Added YahooOpenIDService. 99 | 100 | ### Version 2.0.0 (03.09.2013) 101 | * Use curl for http requests by default. 102 | * getIsAuthenticated() function now looks up for existing access token for all OAuth services. 103 | * Added support for oauth_expires_in to OAuth1 services. 104 | * Added error handlers to OAuth1 services. 105 | * Added support for refresh tokens to OAuth2 ServiceProxy. 106 | * Added an option to disable OAuth2 state validation. 107 | 108 | ### 31.08.2013 109 | * Reorganize directories. Separate root directory by service type. 110 | * Fixed OAuthService::getCallbackUrl(). Now returns url without GET arguments. 111 | * Fixed typos in OAuth services. 112 | * Fixed OpenID loadAttributes functions. 113 | * OAuth2 display mode handling moved to the base class. 114 | * Added OAuthService::getAccessTokenData() method to access to valid access_token and related data. 115 | * Added token default lifetime setting. 116 | * Added "state" argument handling for OAuth2 services to improve security. 117 | * Updated OpenID library. Fixed error with stream requests. 118 | * Added VKontakteOAuth2Service. 119 | * Added GoogleOAuth2Service. 120 | * Added GoogleOAuth2Service. 121 | * Added YandexOAuth2Service. 122 | * Added session token storage using Yii session. 123 | 124 | ### 30.08.2013 125 | * Initial release for Yii2. 126 | -------------------------------------------------------------------------------- /src/oauth/SessionTokenStorage.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth; 11 | 12 | use Yii; 13 | use OAuth\Common\Storage\TokenStorageInterface; 14 | use OAuth\Common\Token\TokenInterface; 15 | use OAuth\Common\Storage\Exception\TokenNotFoundException; 16 | use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException; 17 | 18 | /** 19 | * Stores a token in a PHP session. 20 | */ 21 | class SessionTokenStorage implements TokenStorageInterface 22 | { 23 | 24 | const SESSION_TOKEN_PREFIX = 'eauth-token-'; 25 | const SESSION_STATE_PREFIX = 'eauth-state-'; 26 | 27 | /** 28 | * @var 29 | */ 30 | protected $componentName; 31 | 32 | /** 33 | * @param string $componentName 34 | */ 35 | public function __construct($componentName = 'session') 36 | { 37 | $this->componentName = $componentName; 38 | } 39 | 40 | /** 41 | * @return null|object 42 | */ 43 | protected function getSession() 44 | { 45 | return Yii::$app->get($this->componentName); 46 | } 47 | 48 | /** 49 | * @param string $service 50 | * @return TokenInterface 51 | * @throws TokenNotFoundException 52 | */ 53 | public function retrieveAccessToken($service) 54 | { 55 | if ($this->hasAccessToken($service)) { 56 | return $this->getSession()->get(self::SESSION_TOKEN_PREFIX . $service); 57 | } 58 | throw new TokenNotFoundException('Token not found in session, are you sure you stored it?'); 59 | } 60 | 61 | /** 62 | * @param string $service 63 | * @param TokenInterface $token 64 | * @return TokenInterface 65 | */ 66 | public function storeAccessToken($service, TokenInterface $token) 67 | { 68 | $this->getSession()->set(self::SESSION_TOKEN_PREFIX . $service, $token); 69 | return $token; 70 | } 71 | 72 | /** 73 | * @param string $service 74 | * @return bool 75 | */ 76 | public function hasAccessToken($service) 77 | { 78 | return $this->getSession()->has(self::SESSION_TOKEN_PREFIX . $service); 79 | } 80 | 81 | /** 82 | * Delete the users token. Aka, log out. 83 | * 84 | * @param string $service 85 | * @return TokenStorageInterface 86 | */ 87 | public function clearToken($service) 88 | { 89 | $this->getSession()->remove(self::SESSION_TOKEN_PREFIX . $service); 90 | return $this; 91 | } 92 | 93 | /** 94 | * Delete *ALL* user tokens. 95 | * 96 | * @return TokenStorageInterface 97 | */ 98 | public function clearAllTokens() 99 | { 100 | $session = $this->getSession(); 101 | foreach ($session as $key => $value) { 102 | if (strpos($key, self::SESSION_TOKEN_PREFIX) === 0) { 103 | $session->remove($key); 104 | } 105 | } 106 | return $this; 107 | } 108 | 109 | /** 110 | * Store the authorization state related to a given service 111 | * 112 | * @param string $service 113 | * @param string $state 114 | * @return TokenStorageInterface 115 | */ 116 | public function storeAuthorizationState($service, $state) 117 | { 118 | $this->getSession()->set(self::SESSION_STATE_PREFIX . $service, $state); 119 | return $this; 120 | } 121 | 122 | /** 123 | * Check if an authorization state for a given service exists 124 | * 125 | * @param string $service 126 | * @return bool 127 | */ 128 | public function hasAuthorizationState($service) 129 | { 130 | return $this->getSession()->has(self::SESSION_STATE_PREFIX . $service); 131 | } 132 | 133 | /** 134 | * Retrieve the authorization state for a given service 135 | * 136 | * @param string $service 137 | * @return string 138 | * @throws AuthorizationStateNotFoundException 139 | */ 140 | public function retrieveAuthorizationState($service) 141 | { 142 | if ($this->hasAuthorizationState($service)) { 143 | return $this->getSession()->get(self::SESSION_STATE_PREFIX . $service); 144 | } 145 | throw new AuthorizationStateNotFoundException('State not found in session, are you sure you stored it?'); 146 | } 147 | 148 | /** 149 | * Clear the authorization state of a given service 150 | * 151 | * @param string $service 152 | * @return TokenStorageInterface 153 | */ 154 | public function clearAuthorizationState($service) 155 | { 156 | $this->getSession()->remove(self::SESSION_STATE_PREFIX . $service); 157 | return $this; 158 | } 159 | 160 | /** 161 | * Delete *ALL* user authorization states. Use with care. Most of the time you will likely 162 | * want to use clearAuthorizationState() instead. 163 | * 164 | * @return TokenStorageInterface 165 | */ 166 | public function clearAllAuthorizationStates() 167 | { 168 | $session = $this->getSession(); 169 | foreach ($session as $key => $value) { 170 | if (strpos($key, self::SESSION_STATE_PREFIX) === 0) { 171 | $session->remove($key); 172 | } 173 | } 174 | return $this; 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /src/openid/Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\openid; 11 | 12 | use \Yii; 13 | use \LightOpenID; 14 | use yii\web\HttpException; 15 | use nodge\eauth\ServiceBase; 16 | use nodge\eauth\IAuthService; 17 | use nodge\eauth\ErrorException; 18 | 19 | /** 20 | * EOpenIDService is a base class for all OpenID providers. 21 | * 22 | * @package application.extensions.eauth 23 | */ 24 | abstract class Service extends ServiceBase implements IAuthService 25 | { 26 | 27 | /** 28 | * @var string a pattern that represents the part of URL-space for which an OpenID Authentication request is valid. 29 | * See the spec for more info: http://openid.net/specs/openid-authentication-2_0.html#realms 30 | * Note: a pattern can be without http(s):// part 31 | */ 32 | public $realm; 33 | 34 | /** 35 | * @var LightOpenID the openid library instance. 36 | */ 37 | private $auth; 38 | 39 | /** 40 | * @var string the OpenID authorization url. 41 | */ 42 | protected $url; 43 | 44 | /** 45 | * @var array the OpenID required attributes. 46 | */ 47 | protected $requiredAttributes = []; 48 | 49 | /** 50 | * @var array the OpenID optional attributes. 51 | */ 52 | protected $optionalAttributes = []; 53 | 54 | 55 | /** 56 | * Initialize the component. 57 | */ 58 | public function init() 59 | { 60 | parent::init(); 61 | $this->auth = new LightOpenID(Yii::$app->getRequest()->getHostInfo()); 62 | } 63 | 64 | /** 65 | * Authenticate the user. 66 | * 67 | * @return boolean whether user was successfuly authenticated. 68 | * @throws ErrorException 69 | * @throws HttpException 70 | */ 71 | public function authenticate() 72 | { 73 | if (!empty($_REQUEST['openid_mode'])) { 74 | switch ($_REQUEST['openid_mode']) { 75 | case 'id_res': 76 | $this->id_res(); 77 | return true; 78 | break; 79 | 80 | case 'cancel': 81 | $this->cancel(); 82 | break; 83 | 84 | default: 85 | throw new HttpException(400, Yii::t('yii', 'Your request is invalid.')); 86 | break; 87 | } 88 | } else { 89 | $this->processRequest(); 90 | } 91 | 92 | return false; 93 | } 94 | 95 | /** 96 | * @throws ErrorException 97 | */ 98 | protected function id_res() 99 | { 100 | try { 101 | if ($this->auth->validate()) { 102 | $this->attributes['id'] = $this->auth->identity; 103 | $this->loadRequiredAttributes(); 104 | $this->loadOptionalAttributes(); 105 | $this->authenticated = true; 106 | } else { 107 | throw new ErrorException(Yii::t('eauth', 'Unable to complete the authentication because the required data was not received.', ['provider' => $this->getServiceTitle()])); 108 | } 109 | } catch (\Exception $e) { 110 | throw new ErrorException($e->getMessage(), $e->getCode()); 111 | } 112 | } 113 | 114 | /** 115 | * @throws ErrorException 116 | */ 117 | protected function loadOptionalAttributes() 118 | { 119 | $attributes = $this->auth->getAttributes(); 120 | foreach ($this->optionalAttributes as $key => $attr) { 121 | if (isset($attributes[$attr[1]])) { 122 | $this->attributes[$key] = $attributes[$attr[1]]; 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * 129 | */ 130 | protected function loadRequiredAttributes() 131 | { 132 | $attributes = $this->auth->getAttributes(); 133 | foreach ($this->requiredAttributes as $key => $attr) { 134 | if (isset($attributes[$attr[1]])) { 135 | $this->attributes[$key] = $attributes[$attr[1]]; 136 | } else { 137 | throw new ErrorException(Yii::t('eauth', 'Unable to complete the authentication because the required data was not received.', ['provider' => $this->getServiceTitle()])); 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * @throws ErrorException 144 | */ 145 | protected function processRequest() 146 | { 147 | $this->auth->identity = $this->url; //Setting identifier 148 | 149 | $this->auth->required = []; //Try to get info from openid provider 150 | foreach ($this->requiredAttributes as $attribute) { 151 | $this->auth->required[$attribute[0]] = $attribute[1]; 152 | } 153 | foreach ($this->optionalAttributes as $attribute) { 154 | $this->auth->required[$attribute[0]] = $attribute[1]; 155 | } 156 | 157 | $this->auth->realm = $this->getRealm(); 158 | $this->auth->returnUrl = Yii::$app->getRequest()->getHostInfo() . Yii::$app->getRequest()->getUrl(); //getting return URL 159 | 160 | try { 161 | $url = $this->auth->authUrl(); 162 | Yii::$app->getResponse()->redirect($url)->send(); 163 | } catch (\Exception $e) { 164 | throw new ErrorException($e->getMessage(), $e->getCode()); 165 | } 166 | } 167 | 168 | /** 169 | * @return string 170 | */ 171 | protected function getRealm() 172 | { 173 | if (isset($this->realm)) { 174 | if (!preg_match('#^[a-z]+\://#', $this->realm)) { 175 | return 'http' . (Yii::$app->getRequest()->getIsSecureConnection() ? 's' : '') . '://' . $this->realm; 176 | } else { 177 | return $this->realm; 178 | } 179 | } else { 180 | return Yii::$app->getRequest()->getHostInfo(); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/oauth/ServiceBase.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth; 11 | 12 | use Yii; 13 | use OAuth\Common\Storage\TokenStorageInterface; 14 | use nodge\eauth\EAuth; 15 | use nodge\eauth\IAuthService; 16 | use nodge\eauth\ErrorException; 17 | use yii\helpers\ArrayHelper; 18 | use yii\helpers\Url; 19 | 20 | /** 21 | * EOAuthService is a base class for all OAuth providers. 22 | * 23 | * @package application.extensions.eauth 24 | */ 25 | abstract class ServiceBase extends \nodge\eauth\ServiceBase implements IAuthService 26 | { 27 | 28 | /** 29 | * @var string Base url for API calls. 30 | */ 31 | protected $baseApiUrl; 32 | 33 | /** 34 | * @var int Default token lifetime. Used when service wont provide expires_in param. 35 | */ 36 | protected $tokenDefaultLifetime = null; 37 | 38 | /** 39 | * @var array TokenStorage class. Null means default value from EAuth component config. 40 | */ 41 | protected $tokenStorage; 42 | 43 | /** 44 | * @var TokenStorageInterface 45 | */ 46 | private $_tokenStorage; 47 | 48 | /** 49 | * Initialize the component. 50 | * 51 | * @param EAuth $component the component instance. 52 | * @param array $options properties initialization. 53 | */ 54 | // public function init($component, $options = []) { 55 | // parent::init($component, $options); 56 | // } 57 | 58 | /** 59 | * For OAuth we can check existing access token. 60 | * Useful for API calls. 61 | * 62 | * @return bool 63 | * @throws ErrorException 64 | */ 65 | public function getIsAuthenticated() 66 | { 67 | if (!$this->authenticated) { 68 | try { 69 | $proxy = $this->getProxy(); 70 | $this->authenticated = $proxy->hasValidAccessToken(); 71 | } catch (\OAuth\Common\Exception\Exception $e) { 72 | throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e); 73 | } 74 | } 75 | return parent::getIsAuthenticated(); 76 | } 77 | 78 | /** 79 | * @return \nodge\eauth\oauth1\ServiceProxy|\nodge\eauth\oauth2\ServiceProxy 80 | */ 81 | abstract protected function getProxy(); 82 | 83 | /** 84 | * @return string the current url 85 | */ 86 | protected function getCallbackUrl() 87 | { 88 | return Url::to('', true); 89 | } 90 | 91 | /** 92 | * @param array $config 93 | */ 94 | public function setTokenStorage(array $config) 95 | { 96 | $this->tokenStorage = ArrayHelper::merge($this->tokenStorage, $config); 97 | } 98 | 99 | /** 100 | * @return TokenStorageInterface 101 | */ 102 | protected function getTokenStorage() 103 | { 104 | if (!isset($this->_tokenStorage)) { 105 | $config = $this->tokenStorage; 106 | if (!isset($config)) { 107 | $config = $this->getComponent()->getTokenStorage(); 108 | } 109 | $this->_tokenStorage = Yii::createObject($config); 110 | } 111 | return $this->_tokenStorage; 112 | } 113 | 114 | /** 115 | * @return int 116 | */ 117 | public function getTokenDefaultLifetime() 118 | { 119 | return $this->tokenDefaultLifetime; 120 | } 121 | 122 | /** 123 | * Returns the protected resource. 124 | * 125 | * @param string $url url to request. 126 | * @param array $options HTTP request options. Keys: query, data, headers. 127 | * @param boolean $parseResponse Whether to parse response. 128 | * @return mixed the response. 129 | * @throws ErrorException 130 | */ 131 | public function makeSignedRequest($url, $options = [], $parseResponse = true) 132 | { 133 | if (!$this->getIsAuthenticated()) { 134 | throw new ErrorException(Yii::t('eauth', 'Unable to complete the signed request because the user was not authenticated.'), 401); 135 | } 136 | 137 | return $this->request($url, $options, $parseResponse, function ($url, $method, $headers, $data) { 138 | return $this->getProxy()->request($url, $method, $data, $headers); 139 | }); 140 | } 141 | 142 | /** 143 | * Returns the public resource. 144 | * 145 | * @param string $url url to request. 146 | * @param array $options HTTP request options. Keys: query, data, headers. 147 | * @param boolean $parseResponse Whether to parse response. 148 | * @return mixed the response. 149 | * @throws ErrorException 150 | */ 151 | public function makeRequest($url, $options = [], $parseResponse = true) 152 | { 153 | $headers = isset($options['headers']) ? $options['headers'] : []; 154 | $options['headers'] = array_merge($this->getExtraApiHeaders(), $headers); 155 | 156 | return parent::makeRequest($url, $options, $parseResponse); 157 | } 158 | 159 | /** 160 | * @return array|null An array with valid access_token information. 161 | */ 162 | protected function getAccessTokenData() 163 | { 164 | if (!$this->getIsAuthenticated()) { 165 | return null; 166 | } 167 | 168 | $token = $this->getProxy()->getAccessToken(); 169 | if (!isset($token)) { 170 | return null; 171 | } 172 | 173 | return [ 174 | 'access_token' => $token->getAccessToken(), 175 | 'refresh_token' => $token->getRefreshToken(), 176 | 'expires' => $token->getEndOfLife(), 177 | 'params' => $token->getExtraParams(), 178 | ]; 179 | } 180 | 181 | /** 182 | * @param array $data 183 | * @return string|null 184 | */ 185 | public function getAccessTokenResponseError($data) 186 | { 187 | return isset($data['error']) ? $data['error'] : null; 188 | } 189 | 190 | /** 191 | * Return any additional headers always needed for this service implementation's API calls. 192 | * 193 | * @return array 194 | */ 195 | public function getExtraApiHeaders() 196 | { 197 | return []; 198 | } 199 | } -------------------------------------------------------------------------------- /src/oauth1/ServiceProxy.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth1; 11 | 12 | use OAuth\Common\Consumer\CredentialsInterface; 13 | use OAuth\Common\Http\Client\ClientInterface; 14 | use OAuth\Common\Http\Exception\TokenResponseException; 15 | use OAuth\Common\Http\Uri\Uri; 16 | use OAuth\Common\Http\Uri\UriInterface; 17 | use OAuth\Common\Storage\TokenStorageInterface; 18 | use OAuth\Common\Token\TokenInterface; 19 | use OAuth\OAuth1\Service\AbstractService; 20 | use OAuth\OAuth1\Signature\SignatureInterface; 21 | use OAuth\OAuth1\Token\StdOAuth1Token; 22 | 23 | class ServiceProxy extends AbstractService 24 | { 25 | 26 | /** 27 | * @var Service the currently used service class 28 | */ 29 | protected $service; 30 | 31 | /** 32 | * {@inheritDoc} 33 | */ 34 | public function __construct( 35 | CredentialsInterface $credentials, 36 | ClientInterface $httpClient, 37 | TokenStorageInterface $storage, 38 | SignatureInterface $signature, 39 | UriInterface $baseApiUri = null, 40 | Service $service 41 | ) 42 | { 43 | $this->service = $service; 44 | parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri); 45 | } 46 | 47 | /** 48 | * @return string 49 | */ 50 | public function service() 51 | { 52 | return $this->service->getServiceName(); 53 | } 54 | 55 | /** 56 | * @return StdOAuth1Token 57 | */ 58 | public function retrieveAccessToken() 59 | { 60 | return $this->storage->retrieveAccessToken($this->service()); 61 | } 62 | 63 | /** 64 | * 65 | */ 66 | public function hasValidAccessToken() 67 | { 68 | $serviceName = $this->service(); 69 | 70 | if (!$this->storage->hasAccessToken($serviceName)) { 71 | return false; 72 | } 73 | 74 | /** @var $token StdOAuth1Token */ 75 | $token = $this->storage->retrieveAccessToken($serviceName); 76 | 77 | $params = $token->getExtraParams(); 78 | if (isset($params['is_request_token'])) { 79 | return false; 80 | } 81 | 82 | return $this->checkTokenLifetime($token); 83 | } 84 | 85 | /** 86 | * @param TokenInterface $token 87 | * @return bool 88 | */ 89 | protected function checkTokenLifetime($token) 90 | { 91 | // assume that we have at least a minute to execute a queries. 92 | return $token->getEndOfLife() - 60 > time() 93 | || $token->getEndOfLife() === TokenInterface::EOL_NEVER_EXPIRES 94 | || $token->getEndOfLife() === TokenInterface::EOL_UNKNOWN; 95 | } 96 | 97 | /** 98 | * @return null|TokenInterface 99 | */ 100 | public function getAccessToken() 101 | { 102 | if (!$this->hasValidAccessToken()) { 103 | return null; 104 | } 105 | 106 | $serviceName = $this->service(); 107 | return $this->storage->retrieveAccessToken($serviceName); 108 | } 109 | 110 | /** 111 | * @return UriInterface 112 | */ 113 | public function getRequestTokenEndpoint() 114 | { 115 | return new Uri($this->service->getRequestTokenEndpoint()); 116 | } 117 | 118 | /** 119 | * @return UriInterface 120 | */ 121 | public function getAuthorizationEndpoint() 122 | { 123 | return new Uri($this->service->getAuthorizationEndpoint()); 124 | } 125 | 126 | /** 127 | * @return UriInterface 128 | */ 129 | public function getAccessTokenEndpoint() 130 | { 131 | return new Uri($this->service->getAccessTokenEndpoint()); 132 | } 133 | 134 | /** 135 | * We need a separate request token parser only to verify the `oauth_callback_confirmed` parameter. For the actual 136 | * parsing we can just use the default access token parser. 137 | * 138 | * @param string $responseBody 139 | * @return StdOAuth1Token 140 | * @throws TokenResponseException 141 | */ 142 | protected function parseRequestTokenResponse($responseBody) 143 | { 144 | parse_str($responseBody, $data); 145 | 146 | if (!isset($data) || !is_array($data)) { 147 | throw new TokenResponseException('Unable to parse response.'); 148 | } else if (!isset($data['oauth_callback_confirmed']) || $data['oauth_callback_confirmed'] != 'true') { 149 | throw new TokenResponseException('Error in retrieving token.'); 150 | } 151 | 152 | $data['is_request_token'] = true; 153 | return $this->parseAccessTokenResponse($data); 154 | } 155 | 156 | /** 157 | * @param string|array $responseBody 158 | * @return StdOAuth1Token 159 | * @throws TokenResponseException 160 | */ 161 | protected function parseAccessTokenResponse($responseBody) 162 | { 163 | if (!is_array($responseBody)) { 164 | parse_str($responseBody, $data); 165 | 166 | if (!isset($data) || !is_array($data)) { 167 | throw new TokenResponseException('Unable to parse response.'); 168 | } 169 | } else { 170 | $data = $responseBody; 171 | } 172 | 173 | $error = $this->service->getAccessTokenResponseError($data); 174 | if (isset($error)) { 175 | throw new TokenResponseException('Error in retrieving token: "' . $error . '"'); 176 | } 177 | 178 | $token = new StdOAuth1Token(); 179 | $names = $this->service->getAccessTokenArgumentNames(); 180 | 181 | $token->setRequestToken($data[$names['oauth_token']]); 182 | $token->setRequestTokenSecret($data[$names['oauth_token_secret']]); 183 | $token->setAccessToken($data[$names['oauth_token']]); 184 | $token->setAccessTokenSecret($data[$names['oauth_token_secret']]); 185 | unset($data[$names['oauth_token']], $data[$names['oauth_token_secret']]); 186 | 187 | if (isset($data[$names['oauth_expires_in']])) { 188 | $token->setLifeTime($data[$names['oauth_expires_in']]); 189 | unset($data[$names['oauth_expires_in']]); 190 | } else { 191 | $token->setLifetime($this->service->getTokenDefaultLifetime()); 192 | } 193 | 194 | $token->setExtraParams($data); 195 | 196 | return $token; 197 | } 198 | 199 | /** 200 | * Return any additional headers always needed for this service implementation's API calls. 201 | * 202 | * @return array 203 | */ 204 | protected function getExtraApiHeaders() 205 | { 206 | return $this->service->getExtraApiHeaders(); 207 | } 208 | } -------------------------------------------------------------------------------- /src/oauth2/ServiceProxy.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth2; 11 | 12 | use OAuth\Common\Consumer\CredentialsInterface; 13 | use OAuth\Common\Http\Client\ClientInterface; 14 | use OAuth\Common\Http\Exception\TokenResponseException; 15 | use OAuth\Common\Http\Uri\Uri; 16 | use OAuth\Common\Http\Uri\UriInterface; 17 | use OAuth\Common\Storage\TokenStorageInterface; 18 | use OAuth\Common\Token\TokenInterface; 19 | use OAuth\OAuth2\Service\AbstractService; 20 | use OAuth\OAuth2\Token\StdOAuth2Token; 21 | 22 | class ServiceProxy extends AbstractService 23 | { 24 | 25 | /** 26 | * @var Service the currently used service class 27 | */ 28 | protected $service; 29 | 30 | /** 31 | * @param CredentialsInterface $credentials 32 | * @param ClientInterface $httpClient 33 | * @param TokenStorageInterface $storage 34 | * @param array $scopes 35 | * @param UriInterface $baseApiUri 36 | * @param Service $service 37 | */ 38 | public function __construct( 39 | CredentialsInterface $credentials, 40 | ClientInterface $httpClient, 41 | TokenStorageInterface $storage, 42 | $scopes = [], 43 | UriInterface $baseApiUri = null, 44 | Service $service 45 | ) 46 | { 47 | $this->service = $service; 48 | parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri, $service->getValidateState()); 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function service() 55 | { 56 | return $this->service->getServiceName(); 57 | } 58 | 59 | /** 60 | * Validate scope 61 | * 62 | * @param string $scope 63 | * @return bool 64 | */ 65 | public function isValidScope($scope) 66 | { 67 | $reflectionClass = new \ReflectionClass(get_class($this->service)); 68 | return in_array($scope, $reflectionClass->getConstants(), true); 69 | } 70 | 71 | /** 72 | * @return bool 73 | */ 74 | public function hasValidAccessToken() 75 | { 76 | $serviceName = $this->service(); 77 | 78 | if (!$this->storage->hasAccessToken($serviceName)) { 79 | return false; 80 | } 81 | 82 | /** @var $token StdOAuth2Token */ 83 | $token = $this->storage->retrieveAccessToken($serviceName); 84 | $valid = $this->checkTokenLifetime($token); 85 | 86 | if (!$valid) { 87 | $refreshToken = $token->getRefreshToken(); 88 | if (isset($refreshToken)) { 89 | $token = $this->refreshAccessToken($token); 90 | return $this->checkTokenLifetime($token); 91 | } 92 | } 93 | 94 | return $valid; 95 | } 96 | 97 | /** 98 | * @param TokenInterface $token 99 | * @return bool 100 | */ 101 | protected function checkTokenLifetime($token) 102 | { 103 | // assume that we have at least a minute to execute a queries. 104 | return $token->getEndOfLife() - 60 > time() 105 | || $token->getEndOfLife() === TokenInterface::EOL_NEVER_EXPIRES 106 | || $token->getEndOfLife() === TokenInterface::EOL_UNKNOWN; 107 | } 108 | 109 | /** 110 | * @return null|TokenInterface 111 | */ 112 | public function getAccessToken() 113 | { 114 | if (!$this->hasValidAccessToken()) { 115 | return null; 116 | } 117 | 118 | $serviceName = $this->service(); 119 | return $this->storage->retrieveAccessToken($serviceName); 120 | } 121 | 122 | /** 123 | * @return UriInterface 124 | */ 125 | public function getAuthorizationEndpoint() 126 | { 127 | return new Uri($this->service->getAuthorizationEndpoint()); 128 | } 129 | 130 | /** 131 | * @return UriInterface 132 | */ 133 | public function getAccessTokenEndpoint() 134 | { 135 | return new Uri($this->service->getAccessTokenEndpoint()); 136 | } 137 | 138 | /** 139 | * @param string $responseBody 140 | * @return StdOAuth2Token 141 | * @throws TokenResponseException 142 | */ 143 | protected function parseAccessTokenResponse($responseBody) 144 | { 145 | $data = $this->service->parseAccessTokenResponse($responseBody); 146 | 147 | if (!isset($data) || !is_array($data)) { 148 | throw new TokenResponseException('Unable to parse response.'); 149 | } 150 | 151 | $error = $this->service->getAccessTokenResponseError($data); 152 | if (isset($error)) { 153 | throw new TokenResponseException('Error in retrieving token: "' . $error . '"'); 154 | } 155 | 156 | $token = new StdOAuth2Token(); 157 | $names = $this->service->getAccessTokenArgumentNames(); 158 | 159 | $token->setAccessToken($data[$names['access_token']]); 160 | unset($data[$names['access_token']]); 161 | 162 | if (isset($data[$names['expires_in']])) { 163 | $token->setLifeTime($data[$names['expires_in']]); 164 | unset($data[$names['expires_in']]); 165 | } else { 166 | $token->setLifetime($this->service->getTokenDefaultLifetime()); 167 | } 168 | 169 | if (isset($data[$names['refresh_token']])) { 170 | $token->setRefreshToken($data[$names['refresh_token']]); 171 | unset($data[$names['refresh_token']]); 172 | } 173 | 174 | $token->setExtraParams($data); 175 | 176 | return $token; 177 | } 178 | 179 | /** 180 | * Return any additional headers always needed for this service implementation's OAuth calls. 181 | * 182 | * @return array 183 | */ 184 | protected function getExtraOAuthHeaders() 185 | { 186 | return $this->service->getExtraOAuthHeaders(); 187 | } 188 | 189 | /** 190 | * Return any additional headers always needed for this service implementation's API calls. 191 | * 192 | * @return array 193 | */ 194 | protected function getExtraApiHeaders() 195 | { 196 | return $this->service->getExtraApiHeaders(); 197 | } 198 | 199 | /** 200 | * Returns a class constant from ServiceInterface defining the authorization method used for the API 201 | * Header is the sane default. 202 | * 203 | * @return int 204 | */ 205 | protected function getAuthorizationMethod() 206 | { 207 | return $this->service->getAuthorizationMethod(); 208 | } 209 | 210 | /** 211 | * Returns the url to redirect to for authorization purposes. 212 | * 213 | * @param array $additionalParameters 214 | * @return Uri 215 | */ 216 | public function getAuthorizationUri(array $additionalParameters = []) 217 | { 218 | $parameters = array_merge($additionalParameters, [ 219 | 'type' => 'web_server', 220 | 'client_id' => $this->credentials->getConsumerId(), 221 | 'redirect_uri' => $this->credentials->getCallbackUrl(), 222 | 'response_type' => 'code', 223 | ]); 224 | 225 | $parameters['scope'] = implode($this->service->getScopeSeparator(), $this->scopes); 226 | 227 | if ($this->needsStateParameterInAuthUrl()) { 228 | if (!isset($parameters['state'])) { 229 | $parameters['state'] = $this->generateAuthorizationState(); 230 | } 231 | $this->storeAuthorizationState($parameters['state']); 232 | } 233 | 234 | // Build the url 235 | $url = clone $this->getAuthorizationEndpoint(); 236 | foreach ($parameters as $key => $val) { 237 | $url->addToQuery($key, $val); 238 | } 239 | 240 | return $url; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/services/GoogleOAuth2Service.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://github.com/Nodge/yii2-eauth/ 9 | * @license http://www.opensource.org/licenses/bsd-license.php 10 | */ 11 | 12 | namespace nodge\eauth\services; 13 | 14 | use nodge\eauth\oauth2\Service; 15 | 16 | /** 17 | * Google provider class. 18 | * 19 | * @package application.extensions.eauth.services 20 | */ 21 | class GoogleOAuth2Service extends Service 22 | { 23 | 24 | /** 25 | * Defined scopes - More scopes are listed here: 26 | * https://developers.google.com/oauthplayground/ 27 | */ 28 | 29 | // Basic 30 | const SCOPE_EMAIL = 'email'; 31 | const SCOPE_PROFILE = 'profile'; 32 | 33 | const SCOPE_USERINFO_EMAIL = 'https://www.googleapis.com/auth/userinfo.email'; 34 | const SCOPE_USERINFO_PROFILE = 'https://www.googleapis.com/auth/userinfo.profile'; 35 | 36 | // Google+ 37 | const SCOPE_GPLUS_ME = 'https://www.googleapis.com/auth/plus.me'; 38 | const SCOPE_GPLUS_LOGIN = 'https://www.googleapis.com/auth/plus.login'; 39 | 40 | // Google Drive 41 | const SCOPE_DOCUMENTSLIST = 'https://docs.google.com/feeds/'; 42 | const SCOPE_SPREADSHEETS = 'https://spreadsheets.google.com/feeds/'; 43 | const SCOPE_GOOGLEDRIVE = 'https://www.googleapis.com/auth/drive'; 44 | const SCOPE_DRIVE_APPS = 'https://www.googleapis.com/auth/drive.appdata'; 45 | const SCOPE_DRIVE_APPS_READ_ONLY = 'https://www.googleapis.com/auth/drive.apps.readonly'; 46 | const SCOPE_GOOGLEDRIVE_FILES = 'https://www.googleapis.com/auth/drive.file'; 47 | const SCOPE_DRIVE_METADATA_READ_ONLY = 'https://www.googleapis.com/auth/drive.metadata.readonly'; 48 | const SCOPE_DRIVE_READ_ONLY = 'https://www.googleapis.com/auth/drive.readonly'; 49 | const SCOPE_DRIVE_SCRIPTS = 'https://www.googleapis.com/auth/drive.scripts'; 50 | 51 | // Adwords 52 | const SCOPE_ADSENSE = 'https://www.googleapis.com/auth/adsense'; 53 | const SCOPE_ADWORDS = 'https://adwords.google.com/api/adwords/'; 54 | const SCOPE_GAN = 'https://www.googleapis.com/auth/gan'; // google affiliate network...? 55 | 56 | // Google Analytics 57 | const SCOPE_ANALYTICS = 'https://www.googleapis.com/auth/analytics'; 58 | const SCOPE_ANALYTICS_EDIT = 'https://www.googleapis.com/auth/analytics.edit'; 59 | const SCOPE_ANALYTICS_MANAGE_USERS = 'https://www.googleapis.com/auth/analytics.manage.users'; 60 | const SCOPE_ANALYTICS_READ_ONLY = 'https://www.googleapis.com/auth/analytics.readonly'; 61 | 62 | // Other services 63 | const SCOPE_BOOKS = 'https://www.googleapis.com/auth/books'; 64 | const SCOPE_BLOGGER = 'https://www.googleapis.com/auth/blogger'; 65 | const SCOPE_CALENDAR = 'https://www.googleapis.com/auth/calendar'; 66 | const SCOPE_CONTACT = 'https://www.google.com/m8/feeds/'; 67 | const SCOPE_CHROMEWEBSTORE = 'https://www.googleapis.com/auth/chromewebstore.readonly'; 68 | const SCOPE_GMAIL = 'https://mail.google.com/mail/feed/atom'; 69 | const SCOPE_PICASAWEB = 'https://picasaweb.google.com/data/'; 70 | const SCOPE_SITES = 'https://sites.google.com/feeds/'; 71 | const SCOPE_URLSHORTENER = 'https://www.googleapis.com/auth/urlshortener'; 72 | const SCOPE_WEBMASTERTOOLS = 'https://www.google.com/webmasters/tools/feeds/'; 73 | const SCOPE_TASKS = 'https://www.googleapis.com/auth/tasks'; 74 | 75 | // Cloud services 76 | const SCOPE_CLOUDSTORAGE = 'https://www.googleapis.com/auth/devstorage.read_write'; 77 | const SCOPE_CONTENTFORSHOPPING = 'https://www.googleapis.com/auth/structuredcontent'; // what even is this 78 | const SCOPE_USER_PROVISIONING = 'https://apps-apis.google.com/a/feeds/user/'; 79 | const SCOPE_GROUPS_PROVISIONING = 'https://apps-apis.google.com/a/feeds/groups/'; 80 | const SCOPE_NICKNAME_PROVISIONING = 'https://apps-apis.google.com/a/feeds/alias/'; 81 | 82 | // Old 83 | const SCOPE_ORKUT = 'https://www.googleapis.com/auth/orkut'; 84 | const SCOPE_GOOGLELATITUDE = 85 | 'https://www.googleapis.com/auth/latitude.all.best https://www.googleapis.com/auth/latitude.all.city'; 86 | const SCOPE_OPENID = 'openid'; 87 | 88 | // YouTube 89 | const SCOPE_YOUTUBE_GDATA = 'https://gdata.youtube.com'; 90 | const SCOPE_YOUTUBE_ANALYTICS_MONETARY = 'https://www.googleapis.com/auth/yt-analytics-monetary.readonly'; 91 | const SCOPE_YOUTUBE_ANALYTICS = 'https://www.googleapis.com/auth/yt-analytics.readonly'; 92 | const SCOPE_YOUTUBE = 'https://www.googleapis.com/auth/youtube'; 93 | const SCOPE_YOUTUBE_READ_ONLY = 'https://www.googleapis.com/auth/youtube.readonly'; 94 | const SCOPE_YOUTUBE_UPLOAD = 'https://www.googleapis.com/auth/youtube.upload'; 95 | const SCOPE_YOUTUBE_PATNER = 'https://www.googleapis.com/auth/youtubepartner'; 96 | const SCOPE_YOUTUBE_PARTNER_EDIT = 'https://www.googleapis.com/auth/youtubepartner-channel-edit'; 97 | 98 | // Google Glass 99 | const SCOPE_GLASS_TIMELINE = 'https://www.googleapis.com/auth/glass.timeline'; 100 | const SCOPE_GLASS_LOCATION = 'https://www.googleapis.com/auth/glass.location'; 101 | 102 | protected $name = 'google_oauth'; 103 | protected $title = 'Google'; 104 | protected $type = 'OAuth2'; 105 | protected $jsArguments = ['popup' => ['width' => 500, 'height' => 450]]; 106 | 107 | protected $scopes = [self::SCOPE_USERINFO_PROFILE]; 108 | protected $providerOptions = [ 109 | 'authorize' => 'https://accounts.google.com/o/oauth2/auth', 110 | 'access_token' => 'https://accounts.google.com/o/oauth2/token', 111 | ]; 112 | 113 | protected function fetchAttributes() 114 | { 115 | $info = $this->makeSignedRequest('https://www.googleapis.com/oauth2/v1/userinfo'); 116 | 117 | $this->attributes['id'] = $info['id']; 118 | $this->attributes['name'] = $info['name']; 119 | 120 | if (!empty($info['link'])) { 121 | $this->attributes['url'] = $info['link']; 122 | } 123 | 124 | /*if (!empty($info['gender'])) 125 | $this->attributes['gender'] = $info['gender'] == 'male' ? 'M' : 'F'; 126 | 127 | if (!empty($info['picture'])) 128 | $this->attributes['photo'] = $info['picture']; 129 | 130 | $info['given_name']; // first name 131 | $info['family_name']; // last name 132 | $info['birthday']; // format: 0000-00-00 133 | $info['locale']; // format: en*/ 134 | } 135 | 136 | /** 137 | * Returns the protected resource. 138 | * 139 | * @param string $url url to request. 140 | * @param array $options HTTP request options. Keys: query, data, referer. 141 | * @param boolean $parseResponse Whether to parse response. 142 | * @return mixed the response. 143 | */ 144 | public function makeSignedRequest($url, $options = [], $parseResponse = true) 145 | { 146 | if (!isset($options['query']['alt'])) { 147 | $options['query']['alt'] = 'json'; 148 | } 149 | return parent::makeSignedRequest($url, $options, $parseResponse); 150 | } 151 | 152 | /** 153 | * Returns the error array. 154 | * 155 | * @param array $response 156 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 157 | */ 158 | protected function fetchResponseError($response) 159 | { 160 | if (isset($response['error'])) { 161 | return [ 162 | 'code' => $response['error']['code'], 163 | 'message' => $response['error']['message'], 164 | ]; 165 | } else { 166 | return null; 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /src/oauth/HttpClient.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth; 11 | 12 | use Yii; 13 | use OAuth\Common\Http\Client\AbstractClient; 14 | use OAuth\Common\Http\Exception\TokenResponseException; 15 | use OAuth\Common\Http\Uri\UriInterface; 16 | 17 | /** 18 | * Client implementation for cURL 19 | */ 20 | class HttpClient extends AbstractClient 21 | { 22 | 23 | /** 24 | * If true, explicitly sets cURL to use SSL version 3. Use this if cURL 25 | * compiles with GnuTLS SSL. 26 | * 27 | * @var bool 28 | */ 29 | protected $forceSSL3 = false; 30 | 31 | /** 32 | * If true and you are working in safe_mode environment or inside open_basedir 33 | * it will use streams instead of curl. 34 | * 35 | * @var bool 36 | */ 37 | protected $useStreamsFallback = false; 38 | 39 | /** 40 | * @var UriInterface 41 | */ 42 | protected $endpoint; 43 | 44 | /** 45 | * @var mixed 46 | */ 47 | protected $requestBody; 48 | 49 | /** 50 | * @var array 51 | */ 52 | protected $extraHeaders = []; 53 | 54 | /** 55 | * @var string 56 | */ 57 | protected $method = 'POST'; 58 | 59 | /** 60 | * @param bool $force 61 | */ 62 | public function setForceSSL3($force) 63 | { 64 | $this->forceSSL3 = $force; 65 | } 66 | 67 | /** 68 | * @return boolean 69 | */ 70 | public function getForceSSL3() 71 | { 72 | return $this->forceSSL3; 73 | } 74 | 75 | /** 76 | * @param bool $useStreamsFallback 77 | */ 78 | public function setUseStreamsFallback($useStreamsFallback) 79 | { 80 | $this->useStreamsFallback = $useStreamsFallback; 81 | } 82 | 83 | /** 84 | * @return bool 85 | */ 86 | public function getUseStreamsFallback() 87 | { 88 | return $this->useStreamsFallback; 89 | } 90 | 91 | /** 92 | * Any implementing HTTP providers should send a request to the provided endpoint with the parameters. 93 | * They should return, in string form, the response body and throw an exception on error. 94 | * 95 | * @param UriInterface $endpoint 96 | * @param mixed $requestBody 97 | * @param array $extraHeaders 98 | * @param string $method 99 | * @return string 100 | */ 101 | public function retrieveResponse(UriInterface $endpoint, $requestBody, array $extraHeaders = [], $method = 'POST') 102 | { 103 | $this->endpoint = $endpoint; 104 | $this->requestBody = $requestBody; 105 | $this->extraHeaders = $extraHeaders; 106 | $this->method = $method; 107 | 108 | if ($this->useStreamsFallback && !$this->allowFollowLocation()) { 109 | return $this->streams(); 110 | } 111 | 112 | return $this->curl(); 113 | } 114 | 115 | /** 116 | * @return bool 117 | */ 118 | protected function allowFollowLocation() 119 | { 120 | return !ini_get('safe_mode') && !ini_get('open_basedir'); 121 | } 122 | 123 | /** 124 | * 125 | */ 126 | protected function prepareRequest() 127 | { 128 | $this->method = strtoupper($this->method); 129 | $this->normalizeHeaders($this->extraHeaders); 130 | 131 | if ($this->method === 'GET' && !empty($this->requestBody)) { 132 | throw new \InvalidArgumentException('No body expected for "GET" request.'); 133 | } 134 | 135 | if (!isset($this->extraHeaders['Content-type']) && $this->method === 'POST' && is_array($this->requestBody)) { 136 | $this->extraHeaders['Content-type'] = 'Content-type: application/x-www-form-urlencoded'; 137 | } 138 | 139 | // Some of services requires User-Agent header (e.g. GitHub) 140 | if (!isset($this->extraHeaders['User-Agent'])) { 141 | $this->extraHeaders['User-Agent'] = 'User-Agent: yii2-eauth'; 142 | } 143 | 144 | $this->extraHeaders['Host'] = 'Host: ' . $this->endpoint->getHost(); 145 | $this->extraHeaders['Connection'] = 'Connection: close'; 146 | 147 | if (YII_DEBUG) { 148 | Yii::trace('EAuth http request: ' . PHP_EOL . var_export([ 149 | 'url' => $this->endpoint->getAbsoluteUri(), 150 | 'method' => $this->method, 151 | 'headers' => $this->extraHeaders, 152 | 'body' => $this->requestBody, 153 | ], true), __NAMESPACE__); 154 | } 155 | 156 | if (is_array($this->requestBody)) { 157 | $this->requestBody = http_build_query($this->requestBody, null, '&'); 158 | } 159 | } 160 | 161 | /** 162 | * @return string 163 | * @throws TokenResponseException 164 | */ 165 | protected function curl() 166 | { 167 | $this->prepareRequest(); 168 | 169 | $ch = curl_init(); 170 | curl_setopt($ch, CURLOPT_URL, $this->endpoint->getAbsoluteUri()); 171 | 172 | if ($this->method === 'POST' || $this->method === 'PUT') { 173 | if ($this->method === 'PUT') { 174 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); 175 | } else { 176 | curl_setopt($ch, CURLOPT_POST, true); 177 | } 178 | curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody); 179 | } else { 180 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method); 181 | } 182 | 183 | if ($this->allowFollowLocation() && $this->maxRedirects > 0) { 184 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 185 | curl_setopt($ch, CURLOPT_MAXREDIRS, $this->maxRedirects); 186 | } 187 | 188 | curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); 189 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 190 | curl_setopt($ch, CURLOPT_HEADER, false); 191 | curl_setopt($ch, CURLOPT_HTTPHEADER, $this->extraHeaders); 192 | 193 | if ($this->forceSSL3) { 194 | curl_setopt($ch, CURLOPT_SSLVERSION, 3); 195 | } 196 | 197 | $response = curl_exec($ch); 198 | $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 199 | 200 | if (YII_DEBUG) { 201 | Yii::trace('EAuth http response: ' . PHP_EOL . var_export($response, true), __NAMESPACE__); 202 | } 203 | 204 | if (false === $response) { 205 | $errNo = curl_errno($ch); 206 | $errStr = curl_error($ch); 207 | curl_close($ch); 208 | 209 | if (empty($errStr)) { 210 | $errStr = 'Failed to request resource.'; 211 | } else { 212 | $errStr = 'cURL Error # ' . $errNo . ': ' . $errStr; 213 | } 214 | 215 | Yii::error('EAuth curl error (' . $responseCode . '): ' . $errStr, __NAMESPACE__); 216 | throw new TokenResponseException($errStr, $responseCode); 217 | } 218 | 219 | curl_close($ch); 220 | 221 | return $response; 222 | } 223 | 224 | /** 225 | * @return string 226 | * @throws TokenResponseException 227 | */ 228 | protected function streams() 229 | { 230 | $this->prepareRequest(); 231 | 232 | $context = stream_context_create([ 233 | 'http' => [ 234 | 'method' => $this->method, 235 | 'header' => array_values($this->extraHeaders), 236 | 'content' => $this->requestBody, 237 | 'protocol_version' => '1.1', 238 | 'user_agent' => 'Yii2 EAuth Client', 239 | 'max_redirects' => $this->maxRedirects, 240 | 'timeout' => $this->timeout, 241 | ], 242 | ]); 243 | 244 | $level = error_reporting(0); 245 | $response = file_get_contents($this->endpoint->getAbsoluteUri(), false, $context); 246 | error_reporting($level); 247 | 248 | if (YII_DEBUG) { 249 | Yii::trace('EAuth http response: ' . PHP_EOL . var_export($response, true), __NAMESPACE__); 250 | } 251 | 252 | if (false === $response) { 253 | $lastError = error_get_last(); 254 | 255 | if (is_null($lastError)) { 256 | $errStr = 'Failed to request resource.'; 257 | } else { 258 | $errStr = $lastError['message']; 259 | } 260 | 261 | Yii::error('EAuth streams error: ' . $errStr, __NAMESPACE__); 262 | throw new TokenResponseException($errStr); 263 | } 264 | 265 | return $response; 266 | } 267 | 268 | } 269 | -------------------------------------------------------------------------------- /src/EAuth.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | use Yii; 13 | use yii\base\Object; 14 | use yii\helpers\ArrayHelper; 15 | use yii\helpers\Url; 16 | 17 | /** 18 | * The EAuth class provides simple authentication via OpenID and OAuth providers. 19 | * 20 | * @package application.extensions.eauth 21 | */ 22 | class EAuth extends Object 23 | { 24 | 25 | /** 26 | * @var array Authorization services and their settings. 27 | */ 28 | protected $services = []; 29 | 30 | /** 31 | * @var boolean Whether to use popup window for the authorization dialog. 32 | */ 33 | protected $popup = true; 34 | 35 | /** 36 | * @var string|bool Cache component name to use. False to disable cache. 37 | */ 38 | public $cache = null; 39 | 40 | /** 41 | * @var integer the number of seconds in which the cached value will expire. 0 means never expire. 42 | */ 43 | public $cacheExpire = 0; 44 | 45 | /** 46 | * @var string popup redirect view with custom js code 47 | */ 48 | protected $redirectWidget = '\\nodge\\eauth\\RedirectWidget'; 49 | 50 | /** 51 | * @var array TokenStorage class. 52 | */ 53 | protected $tokenStorage = [ 54 | 'class' => 'nodge\eauth\oauth\SessionTokenStorage', 55 | ]; 56 | 57 | /** 58 | * @var array HttpClient class. 59 | */ 60 | protected $httpClient = [ 61 | 'class' => 'nodge\eauth\oauth\HttpClient', 62 | // 'useStreamsFallback' => false, 63 | ]; 64 | 65 | /** 66 | * Initialize the component. 67 | */ 68 | public function init() 69 | { 70 | parent::init(); 71 | 72 | // set default cache on production environments 73 | if (!isset($this->cache) && YII_ENV_PROD) { 74 | $this->cache = 'cache'; 75 | } 76 | } 77 | 78 | /** 79 | * @param array $services 80 | */ 81 | public function setServices($services) 82 | { 83 | $this->services = $services; 84 | } 85 | 86 | /** 87 | * Returns services settings declared in the authorization classes. 88 | * For perfomance reasons it uses cache to store settings array. 89 | * 90 | * @return \stdClass[] services settings. 91 | */ 92 | public function getServices() 93 | { 94 | $services = false; 95 | if (!empty($this->cache) && Yii::$app->has($this->cache)) { 96 | /** @var $cache \yii\caching\Cache */ 97 | $cache = Yii::$app->get($this->cache); 98 | $services = $cache->get('EAuth.services'); 99 | } 100 | 101 | if (false === $services || !is_array($services)) { 102 | $services = []; 103 | foreach ($this->services as $service => $options) { 104 | /** @var $class ServiceBase */ 105 | $class = $this->getIdentity($service); 106 | $services[$service] = (object)[ 107 | 'id' => $class->getServiceName(), 108 | 'title' => $class->getServiceTitle(), 109 | 'type' => $class->getServiceType(), 110 | 'jsArguments' => $class->getJsArguments(), 111 | ]; 112 | } 113 | if (isset($cache)) { 114 | $cache->set('EAuth.services', $services, $this->cacheExpire); 115 | } 116 | } 117 | return $services; 118 | } 119 | 120 | /** 121 | * @param bool $usePopup 122 | */ 123 | public function setPopup($usePopup) 124 | { 125 | $this->popup = $usePopup; 126 | } 127 | 128 | /** 129 | * @return bool 130 | */ 131 | public function getPopup() 132 | { 133 | return $this->popup; 134 | } 135 | 136 | /** 137 | * @param string|bool $cache 138 | */ 139 | public function setCache($cache) 140 | { 141 | $this->cache = $cache; 142 | } 143 | 144 | /** 145 | * @return string|bool 146 | */ 147 | public function getCache() 148 | { 149 | return $this->cache; 150 | } 151 | 152 | /** 153 | * @param int $cacheExpire 154 | */ 155 | public function setCacheExpire($cacheExpire) 156 | { 157 | $this->cacheExpire = $cacheExpire; 158 | } 159 | 160 | /** 161 | * @return int 162 | */ 163 | public function getCacheExpire() 164 | { 165 | return $this->cacheExpire; 166 | } 167 | 168 | /** 169 | * @param string $redirectWidget 170 | */ 171 | public function setRedirectWidget($redirectWidget) 172 | { 173 | $this->redirectWidget = $redirectWidget; 174 | } 175 | 176 | /** 177 | * @return string 178 | */ 179 | public function getRedirectWidget() 180 | { 181 | return $this->redirectWidget; 182 | } 183 | 184 | /** 185 | * @param array $config 186 | */ 187 | public function setTokenStorage(array $config) 188 | { 189 | $this->tokenStorage = ArrayHelper::merge($this->tokenStorage, $config); 190 | } 191 | 192 | /** 193 | * @return array 194 | */ 195 | public function getTokenStorage() 196 | { 197 | return $this->tokenStorage; 198 | } 199 | 200 | /** 201 | * @param array $config 202 | */ 203 | public function setHttpClient(array $config) 204 | { 205 | $this->httpClient = ArrayHelper::merge($this->httpClient, $config); 206 | } 207 | 208 | /** 209 | * @return array 210 | */ 211 | public function getHttpClient() 212 | { 213 | return $this->httpClient; 214 | } 215 | 216 | /** 217 | * Returns the settings of the service. 218 | * 219 | * @param string $service the service name. 220 | * @return \stdClass the service settings. 221 | * @throws ErrorException 222 | */ 223 | protected function getService($service) 224 | { 225 | $service = strtolower($service); 226 | $services = $this->getServices(); 227 | if (!isset($services[$service])) { 228 | throw new ErrorException(Yii::t('eauth', 'Undefined service name: {service}.', ['service' => $service]), 500); 229 | } 230 | return $services[$service]; 231 | } 232 | 233 | /** 234 | * Returns the type of the service. 235 | * 236 | * @param string $service the service name. 237 | * @return string the service type. 238 | */ 239 | public function getServiceType($service) 240 | { 241 | $service = $this->getService($service); 242 | return $service->type; 243 | } 244 | 245 | /** 246 | * Returns the service identity class. 247 | * 248 | * @param string $service the service name. 249 | * @return IAuthService the identity class. 250 | * @throws ErrorException 251 | */ 252 | public function getIdentity($service) 253 | { 254 | $service = strtolower($service); 255 | if (!isset($this->services[$service])) { 256 | throw new ErrorException(Yii::t('eauth', 'Undefined service name: {service}.', ['service' => $service]), 500); 257 | } 258 | $service = $this->services[$service]; 259 | 260 | $service['component'] = $this; 261 | 262 | /** @var $identity IAuthService */ 263 | $identity = Yii::createObject($service); 264 | return $identity; 265 | } 266 | 267 | /** 268 | * Redirects to url. If the authorization dialog opened in the popup window, 269 | * it will be closed instead of redirect. Set $jsRedirect=true if you want 270 | * to redirect anyway. 271 | * 272 | * @param mixed $url url to redirect. Can be route or normal url. See {@link CHtml::normalizeUrl}. 273 | * @param boolean $jsRedirect whether to use redirect while popup window is used. Defaults to true. 274 | * @param array $params 275 | */ 276 | public function redirect($url, $jsRedirect = true, $params = []) 277 | { 278 | /** @var RedirectWidget $widget */ 279 | $widget = Yii::createObject([ 280 | 'class' => $this->redirectWidget, 281 | 'url' => Url::to($url), 282 | 'redirect' => $jsRedirect, 283 | 'params' => $params 284 | ]); 285 | ob_start(); 286 | $widget->run(); 287 | $output = ob_get_clean(); 288 | $response = Yii::$app->getResponse(); 289 | $response->content = $output; 290 | $response->send(); 291 | exit(); 292 | } 293 | 294 | /** 295 | * Serialize the identity class. 296 | * 297 | * @param ServiceBase $identity the class instance. 298 | * @return string serialized value. 299 | */ 300 | public function toString($identity) 301 | { 302 | return serialize($identity); 303 | } 304 | 305 | /** 306 | * Serialize the identity class. 307 | * 308 | * @param string $identity serialized value. 309 | * @return ServiceBase the class instance. 310 | */ 311 | public function fromString($identity) 312 | { 313 | return unserialize($identity); 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/oauth2/Service.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth\oauth2; 11 | 12 | use Yii; 13 | use yii\helpers\Url; 14 | use OAuth\Common\Exception\Exception as OAuthException; 15 | use OAuth\Common\Http\Uri\Uri; 16 | use OAuth\Common\Consumer\Credentials; 17 | use OAuth\OAuth2\Service\ServiceInterface; 18 | use nodge\eauth\EAuth; 19 | use nodge\eauth\ErrorException; 20 | use nodge\eauth\IAuthService; 21 | use nodge\eauth\oauth\ServiceBase; 22 | 23 | /** 24 | * EOAuthService is a base class for all OAuth providers. 25 | * 26 | * @package application.extensions.eauth 27 | */ 28 | abstract class Service extends ServiceBase implements IAuthService 29 | { 30 | 31 | /** 32 | * @var string OAuth2 client id. 33 | */ 34 | protected $clientId; 35 | 36 | /** 37 | * @var string OAuth2 client secret key. 38 | */ 39 | protected $clientSecret; 40 | 41 | /** 42 | * @var array OAuth scopes. 43 | */ 44 | protected $scopes = []; 45 | 46 | /** 47 | * @var string 48 | */ 49 | protected $scopeSeparator = ' '; 50 | 51 | /** 52 | * @var array Provider options. Must contain the keys: authorize, access_token. 53 | */ 54 | protected $providerOptions = [ 55 | 'authorize' => '', 56 | 'access_token' => '', 57 | ]; 58 | 59 | /** 60 | * @var string Error key name in _GET options. 61 | */ 62 | protected $errorParam = 'error'; 63 | 64 | /** 65 | * @var string Error description key name in _GET options. 66 | */ 67 | protected $errorDescriptionParam = 'error_description'; 68 | 69 | /** 70 | * @var string Error code for access_denied response. 71 | */ 72 | protected $errorAccessDeniedCode = 'access_denied'; 73 | 74 | /** 75 | * @var string The display name for popup window. False to disable display mode. 76 | */ 77 | protected $popupDisplayName = 'popup'; 78 | 79 | /** 80 | * @var bool Whether to use the State param to improve security. 81 | */ 82 | protected $validateState = true; 83 | 84 | /** 85 | * @var ServiceProxy 86 | */ 87 | private $_proxy; 88 | 89 | /** 90 | * Initialize the component. 91 | * 92 | * @param EAuth $component the component instance. 93 | * @param array $options properties initialization. 94 | */ 95 | // public function init($component, $options = []) { 96 | // parent::init($component, $options); 97 | // } 98 | 99 | /** 100 | * @param string $id 101 | */ 102 | public function setClientId($id) 103 | { 104 | $this->clientId = $id; 105 | } 106 | 107 | /** 108 | * @param string $secret 109 | */ 110 | public function setClientSecret($secret) 111 | { 112 | $this->clientSecret = $secret; 113 | } 114 | 115 | /** 116 | * @param string|array $scopes 117 | */ 118 | public function setScope($scopes) 119 | { 120 | if (!is_array($scopes)) { 121 | $scopes = [$scopes]; 122 | } 123 | 124 | $resolvedScopes = []; 125 | $reflClass = new \ReflectionClass($this); 126 | $constants = $reflClass->getConstants(); 127 | 128 | foreach ($scopes as $scope) { 129 | $key = strtoupper('SCOPE_' . $scope); 130 | 131 | // try to find a class constant with this name 132 | if (array_key_exists($key, $constants)) { 133 | $resolvedScopes[] = $constants[$key]; 134 | } else { 135 | $resolvedScopes[] = $scope; 136 | } 137 | } 138 | 139 | $this->scopes = $resolvedScopes; 140 | } 141 | 142 | /** 143 | * @param bool $validate 144 | */ 145 | public function setValidateState($validate) 146 | { 147 | $this->validateState = $validate; 148 | } 149 | 150 | /** 151 | * @return bool 152 | */ 153 | public function getValidateState() 154 | { 155 | return $this->validateState; 156 | } 157 | 158 | /** 159 | * @return ServiceProxy 160 | */ 161 | protected function getProxy() 162 | { 163 | if (!isset($this->_proxy)) { 164 | $tokenStorage = $this->getTokenStorage(); 165 | $httpClient = $this->getHttpClient(); 166 | $credentials = new Credentials($this->clientId, $this->clientSecret, $this->getCallbackUrl()); 167 | $this->_proxy = new ServiceProxy($credentials, $httpClient, $tokenStorage, $this->scopes, null, $this); 168 | } 169 | return $this->_proxy; 170 | } 171 | 172 | /** 173 | * @return string the current url 174 | */ 175 | protected function getCallbackUrl() 176 | { 177 | if (isset($_GET['redirect_uri'])) { 178 | $url = $_GET['redirect_uri']; 179 | } 180 | else { 181 | $route = Yii::$app->getRequest()->getQueryParams(); 182 | array_unshift($route, ''); 183 | 184 | // Can not use these params in OAuth2 callbacks 185 | foreach (['code', 'state', 'redirect_uri'] as $param) { 186 | if (isset($route[$param])) { 187 | unset($route[$param]); 188 | } 189 | } 190 | 191 | $url = Url::to($route, true); 192 | } 193 | 194 | return $url; 195 | } 196 | 197 | /** 198 | * Authenticate the user. 199 | * 200 | * @return boolean whether user was successfuly authenticated. 201 | * @throws ErrorException 202 | */ 203 | public function authenticate() 204 | { 205 | if (!$this->checkError()) { 206 | return false; 207 | } 208 | 209 | try { 210 | $proxy = $this->getProxy(); 211 | 212 | if (!empty($_GET['code'])) { 213 | // This was a callback request from a service, get the token 214 | $proxy->requestAccessToken($_GET['code']); 215 | $this->authenticated = true; 216 | } else if ($proxy->hasValidAccessToken()) { 217 | $this->authenticated = true; 218 | } else { 219 | /** @var $url Uri */ 220 | $url = $proxy->getAuthorizationUri(); 221 | Yii::$app->getResponse()->redirect($url->getAbsoluteUri())->send(); 222 | } 223 | } catch (OAuthException $e) { 224 | throw new ErrorException($e->getMessage(), $e->getCode(), 1, $e->getFile(), $e->getLine(), $e); 225 | } 226 | 227 | return $this->getIsAuthenticated(); 228 | } 229 | 230 | /** 231 | * Check request params for error code and message. 232 | * 233 | * @return bool 234 | * @throws ErrorException 235 | */ 236 | protected function checkError() 237 | { 238 | if (isset($_GET[$this->errorParam])) { 239 | $error_code = $_GET[$this->errorParam]; 240 | if ($error_code === $this->errorAccessDeniedCode) { 241 | // access_denied error (user canceled) 242 | $this->cancel(); 243 | } else { 244 | $error = $error_code; 245 | if (isset($_GET[$this->errorDescriptionParam])) { 246 | $error = $_GET[$this->errorDescriptionParam] . ' (' . $error . ')'; 247 | } 248 | throw new ErrorException($error); 249 | } 250 | return false; 251 | } 252 | 253 | return true; 254 | } 255 | 256 | /** 257 | * @return string 258 | */ 259 | public function getAuthorizationEndpoint() 260 | { 261 | $url = $this->providerOptions['authorize']; 262 | if ($this->popupDisplayName !== false && $this->getIsInsidePopup()) { 263 | $url = new Uri($url); 264 | $url->addToQuery('display', $this->popupDisplayName); 265 | $url = $url->getAbsoluteUri(); 266 | } 267 | return $url; 268 | } 269 | 270 | /** 271 | * @return string 272 | */ 273 | public function getAccessTokenEndpoint() 274 | { 275 | return $this->providerOptions['access_token']; 276 | } 277 | 278 | /** 279 | * @param string $response 280 | * @return array 281 | */ 282 | public function parseAccessTokenResponse($response) 283 | { 284 | return json_decode($response, true); 285 | } 286 | 287 | /** 288 | * @return array 289 | */ 290 | public function getAccessTokenArgumentNames() 291 | { 292 | return [ 293 | 'access_token' => 'access_token', 294 | 'expires_in' => 'expires_in', 295 | 'refresh_token' => 'refresh_token', 296 | ]; 297 | } 298 | 299 | /** 300 | * Return any additional headers always needed for this service implementation's OAuth calls. 301 | * 302 | * @return array 303 | */ 304 | public function getExtraOAuthHeaders() 305 | { 306 | return []; 307 | } 308 | 309 | /** 310 | * Returns a class constant from ServiceInterface defining the authorization method used for the API 311 | * Header is the sane default. 312 | * 313 | * @return int 314 | */ 315 | public function getAuthorizationMethod() 316 | { 317 | return ServiceInterface::AUTHORIZATION_METHOD_HEADER_OAUTH; 318 | } 319 | 320 | /** 321 | * @return string 322 | */ 323 | public function getScopeSeparator() 324 | { 325 | return $this->scopeSeparator; 326 | } 327 | } -------------------------------------------------------------------------------- /src/ServiceBase.php: -------------------------------------------------------------------------------- 1 | 6 | * @link http://github.com/Nodge/yii2-eauth/ 7 | * @license http://www.opensource.org/licenses/bsd-license.php 8 | */ 9 | 10 | namespace nodge\eauth; 11 | 12 | use Yii; 13 | use yii\base\Object; 14 | use yii\helpers\Url; 15 | use yii\helpers\ArrayHelper; 16 | use OAuth\Common\Http\Uri\Uri; 17 | use OAuth\Common\Http\Client\ClientInterface; 18 | 19 | /** 20 | * EAuthServiceBase is a base class for providers. 21 | * 22 | * @package application.extensions.eauth 23 | */ 24 | abstract class ServiceBase extends Object implements IAuthService 25 | { 26 | 27 | /** 28 | * @var string the service name. 29 | */ 30 | protected $name; 31 | 32 | /** 33 | * 34 | * @var string the service title to display in views. 35 | */ 36 | protected $title; 37 | 38 | /** 39 | * @var string the service type (e.g. OpenID, OAuth). 40 | */ 41 | protected $type; 42 | 43 | /** 44 | * @var array arguments for the jQuery.eauth() javascript function. 45 | */ 46 | protected $jsArguments = []; 47 | 48 | /** 49 | * @var array authorization attributes. 50 | * @see getAttribute 51 | * @see getItem 52 | */ 53 | protected $attributes = []; 54 | 55 | /** 56 | * @var boolean whether user was successfuly authenticated. 57 | * @see getIsAuthenticated 58 | */ 59 | protected $authenticated = false; 60 | 61 | /** 62 | * @var array HttpClient class. Null means default value from EAuth component config. 63 | */ 64 | protected $httpClient; 65 | 66 | /** 67 | * @var boolean whether is attributes was fetched. 68 | */ 69 | private $fetched = false; 70 | 71 | /** 72 | * @var EAuth the {@link EAuth} application component. 73 | */ 74 | private $component; 75 | 76 | /** 77 | * @var string the redirect url after successful authorization. 78 | */ 79 | private $redirectUrl = ''; 80 | 81 | /** 82 | * @var string the redirect url after unsuccessful authorization (e.g. user canceled). 83 | */ 84 | private $cancelUrl = ''; 85 | 86 | /** 87 | * @var ClientInterface 88 | */ 89 | private $_httpClient; 90 | 91 | /** 92 | * PHP getter magic method. 93 | * This method is overridden so that service attributes can be accessed like properties. 94 | * 95 | * @param string $name property name. 96 | * @return mixed property value. 97 | * @see getAttribute 98 | */ 99 | public function __get($name) 100 | { 101 | if ($this->hasAttribute($name)) { 102 | return $this->getAttribute($name); 103 | } else { 104 | return parent::__get($name); 105 | } 106 | } 107 | 108 | /** 109 | * Checks if a attribute value is null. 110 | * This method overrides the parent implementation by checking 111 | * if the attribute is null or not. 112 | * 113 | * @param string $name the attribute name. 114 | * @return boolean whether the attribute value is null. 115 | */ 116 | public function __isset($name) 117 | { 118 | if ($this->hasAttribute($name)) { 119 | return true; 120 | } else { 121 | return parent::__isset($name); 122 | } 123 | } 124 | 125 | /** 126 | * Initialize the component. 127 | * Sets the default {@link redirectUrl} and {@link cancelUrl}. 128 | */ 129 | public function init() 130 | { 131 | parent::init(); 132 | 133 | $this->setRedirectUrl(Yii::$app->getUser()->getReturnUrl()); 134 | 135 | $service = Yii::$app->getRequest()->getQueryParam('service'); 136 | $cancelUrl = Url::to(['', 'service' => $service], true); 137 | 138 | $this->setCancelUrl($cancelUrl); 139 | } 140 | 141 | /** 142 | * Returns service name(id). 143 | * 144 | * @return string the service name(id). 145 | */ 146 | public function getServiceName() 147 | { 148 | return $this->name; 149 | } 150 | 151 | /** 152 | * Returns service title. 153 | * 154 | * @return string the service title. 155 | */ 156 | public function getServiceTitle() 157 | { 158 | return Yii::t('eauth', $this->title); 159 | } 160 | 161 | /** 162 | * Returns service type (e.g. OpenID, OAuth). 163 | * 164 | * @return string the service type (e.g. OpenID, OAuth). 165 | */ 166 | public function getServiceType() 167 | { 168 | return $this->type; 169 | } 170 | 171 | /** 172 | * Returns arguments for the jQuery.eauth() javascript function. 173 | * 174 | * @return array the arguments for the jQuery.eauth() javascript function. 175 | */ 176 | public function getJsArguments() 177 | { 178 | return $this->jsArguments; 179 | } 180 | 181 | /** 182 | * Sets {@link EAuth} application component 183 | * 184 | * @param EAuth $component the application auth component. 185 | */ 186 | public function setComponent($component) 187 | { 188 | $this->component = $component; 189 | } 190 | 191 | /** 192 | * Returns the {@link EAuth} application component. 193 | * 194 | * @return EAuth the {@link EAuth} application component. 195 | */ 196 | public function getComponent() 197 | { 198 | return $this->component; 199 | } 200 | 201 | /** 202 | * Sets redirect url after successful authorization. 203 | * 204 | * @param string $url to redirect. 205 | */ 206 | public function setRedirectUrl($url) 207 | { 208 | $this->redirectUrl = $url; 209 | } 210 | 211 | /** 212 | * @return string the redirect url after successful authorization. 213 | */ 214 | public function getRedirectUrl() 215 | { 216 | return $this->redirectUrl; 217 | } 218 | 219 | /** 220 | * Sets redirect url after unsuccessful authorization (e.g. user canceled). 221 | * 222 | * @param string $url 223 | */ 224 | public function setCancelUrl($url) 225 | { 226 | $this->cancelUrl = $url; 227 | } 228 | 229 | /** 230 | * @return string the redirect url after unsuccessful authorization (e.g. user canceled). 231 | */ 232 | public function getCancelUrl() 233 | { 234 | return $this->cancelUrl; 235 | } 236 | 237 | /** 238 | * @param string $title 239 | */ 240 | public function setTitle($title) 241 | { 242 | $this->title = $title; 243 | } 244 | 245 | /** 246 | * Authenticate the user. 247 | * 248 | * @return boolean whether user was successfuly authenticated. 249 | */ 250 | public function authenticate() 251 | { 252 | return $this->getIsAuthenticated(); 253 | } 254 | 255 | /** 256 | * Whether user was successfuly authenticated. 257 | * 258 | * @return boolean whether user was successfuly authenticated. 259 | */ 260 | public function getIsAuthenticated() 261 | { 262 | return $this->authenticated; 263 | } 264 | 265 | /** 266 | * Redirect to the url. If url is null, {@link redirectUrl} will be used. 267 | * 268 | * @param string $url url to redirect. 269 | * @param array $params 270 | */ 271 | public function redirect($url = null, $params = []) 272 | { 273 | $this->component->redirect(isset($url) ? $url : $this->redirectUrl, true, $params); 274 | } 275 | 276 | /** 277 | * Redirect to the {@link cancelUrl} or simply close the popup window. 278 | */ 279 | public function cancel($url = null) 280 | { 281 | $this->component->redirect(isset($url) ? $url : $this->cancelUrl, !$this->component->popup); 282 | } 283 | 284 | /** 285 | * Fetch attributes array. 286 | * 287 | * @return boolean whether the attributes was successfully fetched. 288 | */ 289 | protected function fetchAttributes() 290 | { 291 | return true; 292 | } 293 | 294 | /** 295 | * Fetch attributes array. 296 | * This function is internally used to handle fetched state. 297 | */ 298 | protected function _fetchAttributes() 299 | { 300 | if (!$this->fetched) { 301 | $this->fetched = true; 302 | $result = $this->fetchAttributes(); 303 | if (isset($result)) { 304 | $this->fetched = $result; 305 | } 306 | } 307 | } 308 | 309 | /** 310 | * Returns the user unique id. 311 | * 312 | * @return mixed the user id. 313 | */ 314 | public function getId() 315 | { 316 | $this->_fetchAttributes(); 317 | return $this->attributes['id']; 318 | } 319 | 320 | /** 321 | * Returns the array that contains all available authorization attributes. 322 | * 323 | * @return array the attributes. 324 | */ 325 | public function getAttributes() 326 | { 327 | $this->_fetchAttributes(); 328 | $attributes = []; 329 | foreach ($this->attributes as $key => $val) { 330 | $attributes[$key] = $this->getAttribute($key); 331 | } 332 | return $attributes; 333 | } 334 | 335 | /** 336 | * Returns the authorization attribute value. 337 | * 338 | * @param string $key the attribute name. 339 | * @param mixed $default the default value. 340 | * @return mixed the attribute value. 341 | */ 342 | public function getAttribute($key, $default = null) 343 | { 344 | $this->_fetchAttributes(); 345 | $getter = 'get' . $key; 346 | if (method_exists($this, $getter)) { 347 | return $this->$getter(); 348 | } else { 349 | return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; 350 | } 351 | } 352 | 353 | /** 354 | * Whether the authorization attribute exists. 355 | * 356 | * @param string $key the attribute name. 357 | * @return boolean true if attribute exists, false otherwise. 358 | */ 359 | public function hasAttribute($key) 360 | { 361 | $this->_fetchAttributes(); 362 | return isset($this->attributes[$key]); 363 | } 364 | 365 | /** 366 | * @return bool 367 | */ 368 | public function getIsInsidePopup() 369 | { 370 | return isset($_GET['js']); 371 | } 372 | 373 | /** 374 | * @return ClientInterface 375 | */ 376 | protected function getHttpClient() 377 | { 378 | if (!isset($this->_httpClient)) { 379 | $config = $this->httpClient; 380 | if (!isset($config)) { 381 | $config = $this->getComponent()->getHttpClient(); 382 | } 383 | $this->_httpClient = Yii::createObject($config); 384 | } 385 | return $this->_httpClient; 386 | } 387 | 388 | /** 389 | * @param array $config 390 | */ 391 | public function setHttpClient(array $config) 392 | { 393 | $this->httpClient = ArrayHelper::merge($this->httpClient, $config); 394 | } 395 | 396 | /** 397 | * Returns the public resource. 398 | * 399 | * @param string $url url to request. 400 | * @param array $options HTTP request options. Keys: query, data, headers. 401 | * @param boolean $parseResponse Whether to parse response. 402 | * @return mixed the response. 403 | * @throws ErrorException 404 | */ 405 | public function makeRequest($url, $options = [], $parseResponse = true) 406 | { 407 | return $this->request($url, $options, $parseResponse, function ($url, $method, $headers, $data) { 408 | return $this->getHttpClient()->retrieveResponse($url, $data, $headers, $method); 409 | }); 410 | } 411 | 412 | /** 413 | * @param string $url 414 | * @param array $options 415 | * @param boolean $parseResponse 416 | * @param callable $fn 417 | * @return mixed 418 | * @throws ErrorException 419 | */ 420 | protected function request($url, $options, $parseResponse, $fn) 421 | { 422 | if (stripos($url, 'http') !== 0) { 423 | $url = $this->baseApiUrl . $url; 424 | } 425 | 426 | $url = new Uri($url); 427 | if (isset($options['query'])) { 428 | foreach ($options['query'] as $key => $value) { 429 | $url->addToQuery($key, $value); 430 | } 431 | } 432 | 433 | $data = isset($options['data']) ? $options['data'] : []; 434 | $method = !empty($data) ? 'POST' : 'GET'; 435 | $headers = isset($options['headers']) ? $options['headers'] : []; 436 | 437 | $response = $fn($url, $method, $headers, $data); 438 | 439 | if ($parseResponse) { 440 | $response = $this->parseResponseInternal($response); 441 | } 442 | 443 | return $response; 444 | } 445 | 446 | /** 447 | * Parse response and check for errors. 448 | * 449 | * @param string $response 450 | * @return mixed 451 | * @throws ErrorException 452 | */ 453 | protected function parseResponseInternal($response) 454 | { 455 | try { 456 | $result = $this->parseResponse($response); 457 | if (!isset($result)) { 458 | throw new ErrorException(Yii::t('eauth', 'Invalid response format.'), 500); 459 | } 460 | 461 | $error = $this->fetchResponseError($result); 462 | if (isset($error) && !empty($error['message'])) { 463 | throw new ErrorException($error['message'], $error['code']); 464 | } 465 | 466 | return $result; 467 | } catch (\Exception $e) { 468 | throw new ErrorException($e->getMessage(), $e->getCode()); 469 | } 470 | } 471 | 472 | /** 473 | * @param string $response 474 | * @return mixed 475 | */ 476 | protected function parseResponse($response) 477 | { 478 | return json_decode($response, true); 479 | } 480 | 481 | /** 482 | * Returns the error array. 483 | * 484 | * @param array $response 485 | * @return array the error array with 2 keys: code and message. Should be null if no errors. 486 | */ 487 | protected function fetchResponseError($response) 488 | { 489 | if (isset($response['error'])) { 490 | return [ 491 | 'code' => 500, 492 | 'message' => 'Unknown error occurred.', 493 | ]; 494 | } 495 | return null; 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2 EAuth extension 2 | ==================== 3 | 4 | EAuth extension allows to authenticate users with accounts on other websites. 5 | Supported protocols: OpenID, OAuth 1.0 and OAuth 2.0. 6 | 7 | EAuth is an extension to provide a unified (does not depend on the selected service) method to authenticate the user. The extension itself does not perform login, does not register the user and does not bind the user accounts from different providers. 8 | 9 | * [Demo](http://nodge.ru/yii-eauth/demo2/) 10 | * [Demo project](https://github.com/Nodge/yii2-eauth-demo/) 11 | * [Installation](#installation) 12 | * [Version for yii 1.1](https://github.com/Nodge/yii-eauth/) 13 | 14 | ### Why own extension and not a third-party service? 15 | The implementation of the authorization on your own server has several advantages: 16 | 17 | * Full control over the process: What will be written in the authorization window, what data we get, etc. 18 | * Ability to change the appearance of the widget. 19 | * When logging in via OAuth, it is possible to invoke methods on the API. 20 | * Fewer dependencies on third-party services - more reliable application. 21 | 22 | 23 | ### The extension allows you to: 24 | 25 | * Ignore the nuances of authorization through the different types of services and use the class based adapters for each service. 26 | * Get a unique user ID that can be used to register the user in your application. 27 | * Extend the standard authorization classes to obtain additional data about the user. 28 | * Work with the API of social networks by extending the authorization classes. 29 | * Set up a list of supported services, customize the appearance of the widget, use the popup window without closing your application. 30 | 31 | 32 | ### Extension includes: 33 | 34 | * The component that contains utility functions. 35 | * A widget that displays a list of services in the form of icons and allowing authorization in the popup window. 36 | * Base classes to create your own services. 37 | * Ready to authenticate via Google, Twitter, Facebook and other providers. 38 | 39 | 40 | ### Included services: 41 | 42 | * OpenID: 43 | * Yahoo 44 | * Steam 45 | * OAuth1: 46 | * Twitter 47 | * LinkedIn 48 | * OAuth2: 49 | * Google 50 | * Facebook 51 | * Live 52 | * GitHub 53 | * LinkedIn 54 | * Instagram 55 | * Yandex (ru) 56 | * VKontake (ru) 57 | * Mail.ru (ru) 58 | * Odnoklassniki (ru) 59 | 60 | 61 | ### Resources 62 | 63 | * [Yii EAuth](https://github.com/Nodge/yii2-eauth) 64 | * [Demo](http://nodge.ru/yii-eauth/demo2/) 65 | * [Demo project](https://github.com/Nodge/yii2-eauth-demo/) 66 | * [Yii Framework](http://yiiframework.com/) 67 | * [OpenID](http://openid.net/) 68 | * [OAuth](http://oauth.net/) 69 | * [OAuth 2.0](http://oauth.net/2/) 70 | * [LightOpenID](https://github.com/iignatov/LightOpenID) 71 | * [PHPoAuthLib](https://github.com/Lusitanian/PHPoAuthLib) 72 | 73 | 74 | ### Requirements 75 | 76 | * Yii 2.0 or above 77 | * curl php extension 78 | * LightOpenId 79 | * PHPoAuthLib 80 | 81 | 82 | # Installation 83 | 84 | This library can be found on [Packagist](https://packagist.org/packages/nodge/yii2-eauth). 85 | The recommended way to install this is through [composer](http://getcomposer.org). 86 | 87 | Edit your `composer.json` and add: 88 | 89 | ```json 90 | { 91 | "require": { 92 | "nodge/yii2-eauth": "~2.0" 93 | } 94 | } 95 | ``` 96 | 97 | And install dependencies: 98 | 99 | ```bash 100 | $ curl -sS https://getcomposer.org/installer | php 101 | $ php composer.phar install 102 | ``` 103 | 104 | 105 | # Usage 106 | 107 | ## Demo project 108 | 109 | The source code of the [demo](http://nodge.ru/yii-eauth/demo2/) is available [here](https://github.com/Nodge/yii2-eauth-demo/). 110 | 111 | 112 | ## Basic setup 113 | 114 | ### Configuration 115 | 116 | Add the following in your config: 117 | 118 | ```php 119 | [ 122 | 'eauth' => [ 123 | 'class' => 'nodge\eauth\EAuth', 124 | 'popup' => true, // Use the popup window instead of redirecting. 125 | 'cache' => false, // Cache component name or false to disable cache. Defaults to 'cache' on production environments. 126 | 'cacheExpire' => 0, // Cache lifetime. Defaults to 0 - means unlimited. 127 | 'httpClient' => [ 128 | // uncomment this to use streams in safe_mode 129 | //'useStreamsFallback' => true, 130 | ], 131 | 'services' => [ // You can change the providers and their classes. 132 | 'google' => [ 133 | // register your app here: https://code.google.com/apis/console/ 134 | 'class' => 'nodge\eauth\services\GoogleOAuth2Service', 135 | 'clientId' => '...', 136 | 'clientSecret' => '...', 137 | 'title' => 'Google', 138 | ], 139 | 'twitter' => [ 140 | // register your app here: https://dev.twitter.com/apps/new 141 | 'class' => 'nodge\eauth\services\TwitterOAuth1Service', 142 | 'key' => '...', 143 | 'secret' => '...', 144 | ], 145 | 'yandex' => [ 146 | // register your app here: https://oauth.yandex.ru/client/my 147 | 'class' => 'nodge\eauth\services\YandexOAuth2Service', 148 | 'clientId' => '...', 149 | 'clientSecret' => '...', 150 | 'title' => 'Yandex', 151 | ], 152 | 'facebook' => [ 153 | // register your app here: https://developers.facebook.com/apps/ 154 | 'class' => 'nodge\eauth\services\FacebookOAuth2Service', 155 | 'clientId' => '...', 156 | 'clientSecret' => '...', 157 | ], 158 | 'yahoo' => [ 159 | 'class' => 'nodge\eauth\services\YahooOpenIDService', 160 | //'realm' => '*.example.org', // your domain, can be with wildcard to authenticate on subdomains. 161 | ], 162 | 'linkedin' => [ 163 | // register your app here: https://www.linkedin.com/secure/developer 164 | 'class' => 'nodge\eauth\services\LinkedinOAuth1Service', 165 | 'key' => '...', 166 | 'secret' => '...', 167 | 'title' => 'LinkedIn (OAuth1)', 168 | ], 169 | 'linkedin_oauth2' => [ 170 | // register your app here: https://www.linkedin.com/secure/developer 171 | 'class' => 'nodge\eauth\services\LinkedinOAuth2Service', 172 | 'clientId' => '...', 173 | 'clientSecret' => '...', 174 | 'title' => 'LinkedIn (OAuth2)', 175 | ], 176 | 'github' => [ 177 | // register your app here: https://github.com/settings/applications 178 | 'class' => 'nodge\eauth\services\GitHubOAuth2Service', 179 | 'clientId' => '...', 180 | 'clientSecret' => '...', 181 | ], 182 | 'live' => [ 183 | // register your app here: https://account.live.com/developers/applications/index 184 | 'class' => 'nodge\eauth\services\LiveOAuth2Service', 185 | 'clientId' => '...', 186 | 'clientSecret' => '...', 187 | ], 188 | 'steam' => [ 189 | 'class' => 'nodge\eauth\services\SteamOpenIDService', 190 | //'realm' => '*.example.org', // your domain, can be with wildcard to authenticate on subdomains. 191 | 'apiKey' => '...', // Optional. You can get it here: https://steamcommunity.com/dev/apikey 192 | ], 193 | 'instagram' => [ 194 | // register your app here: https://instagram.com/developer/register/ 195 | 'class' => 'nodge\eauth\services\InstagramOAuth2Service', 196 | 'clientId' => '...', 197 | 'clientSecret' => '...', 198 | ], 199 | 'vkontakte' => [ 200 | // register your app here: https://vk.com/editapp?act=create&site=1 201 | 'class' => 'nodge\eauth\services\VKontakteOAuth2Service', 202 | 'clientId' => '...', 203 | 'clientSecret' => '...', 204 | ], 205 | 'mailru' => [ 206 | // register your app here: http://api.mail.ru/sites/my/add 207 | 'class' => 'nodge\eauth\services\MailruOAuth2Service', 208 | 'clientId' => '...', 209 | 'clientSecret' => '...', 210 | ], 211 | 'odnoklassniki' => [ 212 | // register your app here: http://dev.odnoklassniki.ru/wiki/pages/viewpage.action?pageId=13992188 213 | // ... or here: http://www.odnoklassniki.ru/dk?st.cmd=appsInfoMyDevList&st._aid=Apps_Info_MyDev 214 | 'class' => 'nodge\eauth\services\OdnoklassnikiOAuth2Service', 215 | 'clientId' => '...', 216 | 'clientSecret' => '...', 217 | 'clientPublic' => '...', 218 | 'title' => 'Odnoklas.', 219 | ], 220 | ], 221 | ], 222 | 223 | 'i18n' => [ 224 | 'translations' => [ 225 | 'eauth' => [ 226 | 'class' => 'yii\i18n\PhpMessageSource', 227 | 'basePath' => '@eauth/messages', 228 | ], 229 | ], 230 | ], 231 | 232 | // (optionally) you can configure pretty urls 233 | 'urlManager' => [ 234 | 'enablePrettyUrl' => true, 235 | 'showScriptName' => false, 236 | 'rules' => [ 237 | 'login/' => 'site/login', 238 | ], 239 | ], 240 | 241 | // (optionally) you can configure logging 242 | 'log' => [ 243 | 'targets' => [ 244 | [ 245 | 'class' => 'yii\log\FileTarget', 246 | 'logFile' => '@app/runtime/logs/eauth.log', 247 | 'categories' => ['nodge\eauth\*'], 248 | 'logVars' => [], 249 | ], 250 | ], 251 | ], 252 | ... 253 | ], 254 | ... 255 | ``` 256 | 257 | ### User model 258 | 259 | You need to modify your User model to login with EAuth services. 260 | Example from demo project: 261 | 262 | ```php 263 | getSession()->has('user-'.$id)) { 272 | return new self(Yii::$app->getSession()->get('user-'.$id)); 273 | } 274 | else { 275 | return isset(self::$users[$id]) ? new self(self::$users[$id]) : null; 276 | } 277 | } 278 | 279 | /** 280 | * @param \nodge\eauth\ServiceBase $service 281 | * @return User 282 | * @throws ErrorException 283 | */ 284 | public static function findByEAuth($service) { 285 | if (!$service->getIsAuthenticated()) { 286 | throw new ErrorException('EAuth user should be authenticated before creating identity.'); 287 | } 288 | 289 | $id = $service->getServiceName().'-'.$service->getId(); 290 | $attributes = [ 291 | 'id' => $id, 292 | 'username' => $service->getAttribute('name'), 293 | 'authKey' => md5($id), 294 | 'profile' => $service->getAttributes(), 295 | ]; 296 | $attributes['profile']['service'] = $service->getServiceName(); 297 | Yii::$app->getSession()->set('user-'.$id, $attributes); 298 | return new self($attributes); 299 | } 300 | ... 301 | ``` 302 | 303 | Then you can access to EAuth attributes through: 304 | 305 | ```php 306 | getUser()->getIdentity(); 308 | if (isset($identity->profile)) { 309 | VarDumper::dump($identity->profile, 10, true); 310 | } 311 | ``` 312 | 313 | ### Controller 314 | 315 | Attach OpenID Controller behavior to disable CSRF validation for OpenID callbacks. 316 | Or you can disable CSRF validation by yourself. 317 | 318 | ```php 319 | [ 324 | // required to disable csrf validation on OpenID requests 325 | 'class' => \nodge\eauth\openid\ControllerBehavior::className(), 326 | 'only' => ['login'], 327 | ], 328 | ]; 329 | } 330 | ... 331 | ``` 332 | 333 | Add the following to your Login action: 334 | 335 | ```php 336 | getRequest()->getQueryParam('service'); 340 | if (isset($serviceName)) { 341 | /** @var $eauth \nodge\eauth\ServiceBase */ 342 | $eauth = Yii::$app->get('eauth')->getIdentity($serviceName); 343 | $eauth->setRedirectUrl(Yii::$app->getUser()->getReturnUrl()); 344 | $eauth->setCancelUrl(Yii::$app->getUrlManager()->createAbsoluteUrl('site/login')); 345 | 346 | try { 347 | if ($eauth->authenticate()) { 348 | // var_dump($eauth->getIsAuthenticated(), $eauth->getAttributes()); exit; 349 | 350 | $identity = User::findByEAuth($eauth); 351 | Yii::$app->getUser()->login($identity); 352 | 353 | // special redirect with closing popup window 354 | $eauth->redirect(); 355 | } 356 | else { 357 | // close popup window and redirect to cancelUrl 358 | $eauth->cancel(); 359 | } 360 | } 361 | catch (\nodge\eauth\ErrorException $e) { 362 | // save error to show it later 363 | Yii::$app->getSession()->setFlash('error', 'EAuthException: '.$e->getMessage()); 364 | 365 | // close popup window and redirect to cancelUrl 366 | // $eauth->cancel(); 367 | $eauth->redirect($eauth->getCancelUrl()); 368 | } 369 | } 370 | 371 | // default authorization code through login/password .. 372 | } 373 | ... 374 | ``` 375 | 376 | ### View 377 | 378 | ```php 379 | ... 380 | getSession()->hasFlash('error')) { 382 | echo '
'.Yii::$app->getSession()->getFlash('error').'
'; 383 | } 384 | ?> 385 | ... 386 |

Do you already have an account on one of these sites? Click the logo to log in with it here:

387 | 'site/login']); ?> 388 | ... 389 | ``` 390 | 391 | 392 | ## Extending 393 | 394 | To receive all the necessary data to your application, you can override the base class of any provider. 395 | Base classes are stored in `@eauth/src/services`. 396 | Examples of extended classes can be found in `@eauth/src/services/extended/`. 397 | 398 | After overriding the base class, you need to update your configuration file with a new class name. 399 | 400 | 401 | ## Working with OAuth API 402 | 403 | You can extend base classes with necessary methods and then write something like this: 404 | 405 | ```php 406 | eauth->getIdentity('facebook'); 409 | 410 | // to get protected resources user should be authenticated: 411 | if ($eauth->getIsAuthenticated()) { 412 | $eauth->callProtectedApiMethod(); 413 | $eauth->callAnotherProtectedApiMethod(); 414 | } 415 | 416 | // or you can get public resources at any time: 417 | $eauth->callPublicApiMethod(); 418 | $eauth->callAnotherPublicApiMethod(); 419 | ``` 420 | 421 | Example of an API call method: 422 | 423 | ```php 424 | makeSignedRequest($api_method, [ 432 | 'query' => [ 'foo' => 'bar' ], // GET arguments 433 | 'data' => [ 'foo' => 'bar' ], // POST arguments 434 | 'headers' => [ 'X-Foo' => 'bar' ], // Extra HTTP headers 435 | ]); 436 | 437 | // you can get public resources with the same API: 438 | //$response = $this->makeRequest($api_method, $options); 439 | 440 | // process $response 441 | $data = process($response); 442 | 443 | // return results 444 | return $data; 445 | } 446 | } 447 | ``` 448 | 449 | API calls are performed if the current user has a valid access token (saved during the authentication). 450 | You can save access_token to your database by using custom token storage in your config: 451 | 452 | ```php 453 | [ 456 | 'eauth' => [ 457 | 'class' => 'nodge\eauth\EAuth', 458 | 'tokenStorage' => [ 459 | 'class' => '@app\eauth\DatabaseTokenStorage', 460 | ], 461 | ], 462 | ... 463 | ], 464 | ... 465 | ``` 466 | 467 | 468 | ## Translation 469 | 470 | To use translations, add the following in your config: 471 | 472 | ```php 473 | [ 476 | 'i18n' => [ 477 | 'translations' => [ 478 | 'eauth' => [ 479 | 'class' => 'yii\i18n\PhpMessageSource', 480 | 'basePath' => '@eauth/messages', 481 | ], 482 | ], 483 | ], 484 | ... 485 | ], 486 | ... 487 | ``` 488 | 489 | Available translations can be found in `@eauth/src/messages`. 490 | 491 | 492 | # License 493 | 494 | The extension was released under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php), so you'll find the latest version on [GitHub](https://github.com/Nodge/yii2-eauth). 495 | --------------------------------------------------------------------------------