Commit 5c575335 by Qiang Xue

Merge pull request #1620 from klimov-paul/authclient

Proposal for #66: auth client implementation
parents dba7c02a 8da6f39a
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient;
use yii\base\Action;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;
use yii\web\Response;
use yii\web\HttpException;
use yii\web\NotFoundHttpException;
use Yii;
/**
* AuthAction performs authentication via different auth clients.
* It supports [[OpenId]], [[OAuth1] and [[OAuth2]] client types.
*
* Usage:
* ~~~
* class SiteController extends Controller
* {
* public function actions()
* {
* return [
* 'auth' => [
* 'class' => 'yii\authclient\AuthAction',
* 'successCallback' => [$this, 'successCallback'],
* ],
* ]
* }
*
* public function successCallback($client)
* {
* $atributes = $client->getUserAttributes();
* // user login or signup comes here
* }
* }
* ~~~
*
* Usually authentication via external services is performed inside the popup window.
* This action handles the redirection and closing of popup window correctly.
*
* @see Collection
* @see \yii\authclient\widgets\Choice
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class AuthAction extends Action
{
/**
* @var string name of the auth client collection application component.
* It should point to [[Collection]] instance.
*/
public $clientCollection = 'authClientCollection';
/**
* @var string name of the GET param, which is used to passed auth client id to this action.
* Note: watch for the naming, make sure you do not choose name used in some auth protocol.
*/
public $clientIdGetParamName = 'authclient';
/**
* @var callable PHP callback, which should be triggered in case of successful authentication.
* This callback should accept [[ClientInterface]] instance as an argument.
* For example:
*
* ~~~
* public function onAuthSuccess($client)
* {
* $atributes = $client->getUserAttributes();
* // user login or signup comes here
* }
* ~~~
*
* If this callback returns [[Response]] instance, it will be used as action response,
* otherwise redirection to [[successUrl]] will be performed.
*
*/
public $successCallback;
/**
* @var string the redirect url after successful authorization.
*/
private $_successUrl = '';
/**
* @var string the redirect url after unsuccessful authorization (e.g. user canceled).
*/
private $_cancelUrl = '';
/**
* @var string name or alias of the view file, which should be rendered in order to perform redirection.
* If not set default one will be used.
*/
public $redirectView;
/**
* @param string $url successful URL.
*/
public function setSuccessUrl($url)
{
$this->_successUrl = $url;
}
/**
* @return string successful URL.
*/
public function getSuccessUrl()
{
if (empty($this->_successUrl)) {
$this->_successUrl = $this->defaultSuccessUrl();
}
return $this->_successUrl;
}
/**
* @param string $url cancel URL.
*/
public function setCancelUrl($url)
{
$this->_cancelUrl = $url;
}
/**
* @return string cancel URL.
*/
public function getCancelUrl()
{
if (empty($this->_cancelUrl)) {
$this->_cancelUrl = $this->defaultCancelUrl();
}
return $this->_cancelUrl;
}
/**
* Creates default {@link successUrl} value.
* @return string success URL value.
*/
protected function defaultSuccessUrl()
{
return Yii::$app->getUser()->getReturnUrl();
}
/**
* Creates default {@link cancelUrl} value.
* @return string cancel URL value.
*/
protected function defaultCancelUrl()
{
return Yii::$app->getRequest()->getAbsoluteUrl();
}
/**
* Runs the action.
*/
public function run()
{
if (!empty($_GET[$this->clientIdGetParamName])) {
$clientId = $_GET[$this->clientIdGetParamName];
/** @var \yii\authclient\Collection $collection */
$collection = Yii::$app->getComponent($this->clientCollection);
if (!$collection->hasClient($clientId)) {
throw new NotFoundHttpException("Unknown auth client '{$clientId}'");
}
$client = $collection->getClient($clientId);
return $this->auth($client);
} else {
throw new NotFoundHttpException();
}
}
/**
* @param mixed $client auth client instance.
* @return Response response instance.
* @throws \yii\base\NotSupportedException on invalid client.
*/
protected function auth($client)
{
if ($client instanceof OpenId) {
return $this->authOpenId($client);
} elseif ($client instanceof OAuth2) {
return $this->authOAuth2($client);
} elseif ($client instanceof OAuth1) {
return $this->authOAuth1($client);
} else {
throw new NotSupportedException('Provider "' . get_class($client) . '" is not supported.');
}
}
/**
* This method is invoked in case of successful authentication via auth client.
* @param ClientInterface $client auth client instance.
* @throws InvalidConfigException on invalid success callback.
* @return Response response instance.
*/
protected function authSuccess($client)
{
if (!is_callable($this->successCallback)) {
throw new InvalidConfigException('"' . get_class($this) . '::successCallback" should be a valid callback.');
}
$response = call_user_func($this->successCallback, $client);
if ($response instanceof Response) {
return $response;
}
return $this->redirectSuccess();
}
/**
* Redirect to the given URL or simply close the popup window.
* @param mixed $url URL to redirect, could be a string or array config to generate a valid URL.
* @param boolean $enforceRedirect indicates if redirect should be performed even in case of popup window.
* @return \yii\web\Response response instance.
*/
public function redirect($url, $enforceRedirect = true)
{
$viewFile = $this->redirectView;
if ($viewFile === null) {
$viewFile = __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'redirect.php';
} else {
$viewFile = Yii::getAlias($viewFile);
}
$viewData = [
'url' => $url,
'enforceRedirect' => $enforceRedirect,
];
$response = Yii::$app->getResponse();
$response->content = Yii::$app->getView()->renderFile($viewFile, $viewData);
return $response;
}
/**
* Redirect to the URL. If URL is null, {@link successUrl} will be used.
* @param string $url URL to redirect.
* @return \yii\web\Response response instance.
*/
public function redirectSuccess($url = null)
{
if ($url === null) {
$url = $this->getSuccessUrl();
}
return $this->redirect($url);
}
/**
* Redirect to the {@link cancelUrl} or simply close the popup window.
* @param string $url URL to redirect.
* @return \yii\web\Response response instance.
*/
public function redirectCancel($url = null)
{
if ($url === null) {
$url = $this->getCancelUrl();
}
return $this->redirect($url, false);
}
/**
* Performs OpenID auth flow.
* @param OpenId $client auth client instance.
* @return Response action response.
* @throws Exception on failure.
* @throws HttpException on failure.
*/
protected function authOpenId($client)
{
if (!empty($_REQUEST['openid_mode'])) {
switch ($_REQUEST['openid_mode']) {
case 'id_res':
if ($client->validate()) {
return $this->authSuccess($client);
} else {
throw new HttpException(400, 'Unable to complete the authentication because the required data was not received.');
}
break;
case 'cancel':
$this->redirectCancel();
break;
default:
throw new HttpException(400);
break;
}
} else {
$url = $client->buildAuthUrl();
return Yii::$app->getResponse()->redirect($url);
}
return $this->redirectCancel();
}
/**
* Performs OAuth1 auth flow.
* @param OAuth1 $client auth client instance.
* @return Response action response.
*/
protected function authOAuth1($client)
{
// user denied error
if (isset($_GET['denied'])) {
return $this->redirectCancel();
}
if (isset($_REQUEST['oauth_token'])) {
$oauthToken = $_REQUEST['oauth_token'];
}
if (!isset($oauthToken)) {
// Get request token.
$requestToken = $client->fetchRequestToken();
// Get authorization URL.
$url = $client->buildAuthUrl($requestToken);
// Redirect to authorization URL.
return Yii::$app->getResponse()->redirect($url);
} else {
// Upgrade to access token.
$accessToken = $client->fetchAccessToken();
return $this->authSuccess($client);
}
}
/**
* Performs OAuth2 auth flow.
* @param OAuth2 $client auth client instance.
* @return Response action response.
* @throws \yii\base\Exception on failure.
*/
protected function authOAuth2($client)
{
if (isset($_GET['error'])) {
if ($_GET['error'] == 'access_denied') {
// user denied error
return $this->redirectCancel();
} else {
// request error
if (isset($_GET['error_description'])) {
$errorMessage = $_GET['error_description'];
} elseif (isset($_GET['error_message'])) {
$errorMessage = $_GET['error_message'];
} else {
$errorMessage = http_build_query($_GET);
}
throw new Exception('Auth error: ' . $errorMessage);
}
}
// Get the access_token and save them to the session.
if (isset($_GET['code'])) {
$code = $_GET['code'];
$token = $client->fetchAccessToken($code);
if (!empty($token)) {
return $this->authSuccess($client);
} else {
return $this->redirectCancel();
}
} else {
$url = $client->buildAuthUrl();
return Yii::$app->getResponse()->redirect($url);
}
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient;
use Yii;
use yii\base\Component;
use yii\base\NotSupportedException;
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* BaseClient is a base Auth Client class.
*
* @see ClientInterface
*
* @property string $id auth service id.
* @property string $name auth service name.
* @property string $title auth service title.
* @property array $userAttributes authenticated user attributes.
* @property array $normalizeUserAttributeMap map used to normalize user attributes fetched from
* external auth service in format: rawAttributeName => normalizedAttributeName.
* @property array $viewOptions view options in format: optionName => optionValue.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
abstract class BaseClient extends Component implements ClientInterface
{
/**
* @var string auth service id.
* This value mainly used as HTTP request parameter.
*/
private $_id;
/**
* @var string auth service name.
* This value may be used in database records, CSS files and so on.
*/
private $_name;
/**
* @var string auth service title to display in views.
*/
private $_title;
/**
* @var array authenticated user attributes.
*/
private $_userAttributes;
/**
* @var array map used to normalize user attributes fetched from external auth service
* in format: rawAttributeName => normalizedAttributeName
*/
private $_normalizeUserAttributeMap;
/**
* @var array view options in format: optionName => optionValue
*/
private $_viewOptions;
/**
* @param string $id service id.
*/
public function setId($id)
{
$this->_id = $id;
}
/**
* @return string service id
*/
public function getId()
{
if (empty($this->_id)) {
$this->_id = $this->getName();
}
return $this->_id;
}
/**
* @param string $name service name.
*/
public function setName($name)
{
$this->_name = $name;
}
/**
* @return string service name.
*/
public function getName()
{
if ($this->_name === null) {
$this->_name = $this->defaultName();
}
return $this->_name;
}
/**
* @param string $title service title.
*/
public function setTitle($title)
{
$this->_title = $title;
}
/**
* @return string service title.
*/
public function getTitle()
{
if ($this->_title === null) {
$this->_title = $this->defaultTitle();
}
return $this->_title;
}
/**
* @param array $userAttributes list of user attributes
*/
public function setUserAttributes($userAttributes)
{
$this->_userAttributes = $this->normalizeUserAttributes($userAttributes);
}
/**
* @return array list of user attributes
*/
public function getUserAttributes()
{
if ($this->_userAttributes === null) {
$this->_userAttributes = $this->normalizeUserAttributes($this->initUserAttributes());
}
return $this->_userAttributes;
}
/**
* @param array $normalizeUserAttributeMap normalize user attribute map.
*/
public function setNormalizeUserAttributeMap($normalizeUserAttributeMap)
{
$this->_normalizeUserAttributeMap = $normalizeUserAttributeMap;
}
/**
* @return array normalize user attribute map.
*/
public function getNormalizeUserAttributeMap()
{
if ($this->_normalizeUserAttributeMap === null) {
$this->_normalizeUserAttributeMap = $this->defaultNormalizeUserAttributeMap();
}
return $this->_normalizeUserAttributeMap;
}
/**
* @param array $viewOptions view options in format: optionName => optionValue
*/
public function setViewOptions($viewOptions)
{
$this->_viewOptions = $viewOptions;
}
/**
* @return array view options in format: optionName => optionValue
*/
public function getViewOptions()
{
if ($this->_viewOptions === null) {
$this->_viewOptions = $this->defaultViewOptions();
}
return $this->_viewOptions;
}
/**
* Generates service name.
* @return string service name.
*/
protected function defaultName()
{
return Inflector::camel2id(StringHelper::basename(get_class($this)));
}
/**
* Generates service title.
* @return string service title.
*/
protected function defaultTitle()
{
return StringHelper::basename(get_class($this));
}
/**
* Initializes authenticated user attributes.
* @return array auth user attributes.
*/
protected function initUserAttributes()
{
throw new NotSupportedException('Method "' . get_class($this) . '::' . __FUNCTION__ . '" not implemented.');
}
/**
* Returns the default [[normalizeUserAttributeMap]] value.
* Particular client may override this method in order to provide specific default map.
* @return array normalize attribute map.
*/
protected function defaultNormalizeUserAttributeMap()
{
return [];
}
/**
* Returns the default [[viewOptions]] value.
* Particular client may override this method in order to provide specific default view options.
* @return array list of default [[viewOptions]]
*/
protected function defaultViewOptions()
{
return [];
}
/**
* Normalize given user attributes according to {@link normalizeUserAttributeMap}.
* @param array $attributes raw attributes.
* @return array normalized attributes.
*/
protected function normalizeUserAttributes($attributes)
{
foreach ($this->getNormalizeUserAttributeMap() as $normalizedName => $actualName) {
if (array_key_exists($actualName, $attributes)) {
$attributes[$normalizedName] = $attributes[$actualName];
}
}
return $attributes;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient;
/**
* ClientInterface declares basic interface all Auth clients should follow.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
interface ClientInterface
{
/**
* @param string $id service id.
*/
public function setId($id);
/**
* @return string service id
*/
public function getId();
/**
* @return string service name.
*/
public function getName();
/**
* @param string $name service name.
*/
public function setName($name);
/**
* @return string service title.
*/
public function getTitle();
/**
* @param string $title service title.
*/
public function setTitle($title);
/**
* @return array list of user attributes
*/
public function getUserAttributes();
/**
* @return array view options in format: optionName => optionValue
*/
public function getViewOptions();
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient;
use yii\base\Component;
use yii\base\InvalidParamException;
use Yii;
/**
* Collection is a storage for all auth clients in the application.
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'google' => [
* 'class' => 'yii\authclient\clients\GoogleOpenId'
* ],
* 'facebook' => [
* 'class' => 'yii\authclient\clients\Facebook',
* 'clientId' => 'facebook_client_id',
* 'clientSecret' => 'facebook_client_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @property array $clients list of Auth clients with their configuration in format: 'clientId' => [...]
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Collection extends Component
{
/**
* @var array list of Auth clients with their configuration in format: 'clientId' => [...]
*/
private $_clients = [];
/**
* @param array $clients list of auth clients
*/
public function setClients(array $clients)
{
$this->_clients = $clients;
}
/**
* @return ClientInterface[] list of auth clients.
*/
public function getClients()
{
$clients = [];
foreach ($this->_clients as $id => $client) {
$clients[$id] = $this->getClient($id);
}
return $clients;
}
/**
* @param string $id service id.
* @return ClientInterface auth client instance.
* @throws InvalidParamException on non existing client request.
*/
public function getClient($id)
{
if (!array_key_exists($id, $this->_clients)) {
throw new InvalidParamException("Unknown auth client '{$id}'.");
}
if (!is_object($this->_clients[$id])) {
$this->_clients[$id] = $this->createClient($id, $this->_clients[$id]);
}
return $this->_clients[$id];
}
/**
* Checks if client exists in the hub.
* @param string $id client id.
* @return boolean whether client exist.
*/
public function hasClient($id)
{
return array_key_exists($id, $this->_clients);
}
/**
* Creates auth client instance from its array configuration.
* @param string $id auth client id.
* @param array $config auth client instance configuration.
* @return ClientInterface auth client instance.
*/
protected function createClient($id, $config)
{
$config['id'] = $id;
return Yii::createObject($config);
}
}
\ No newline at end of file
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
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 OWNER 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.
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient;
use Yii;
use yii\base\Exception;
/**
* OAuth2 serves as a client for the OAuth 2 flow.
*
* In oder to acquire access token perform following sequence:
*
* ~~~
* use yii\authclient\OAuth2;
*
* $oauthClient = new OAuth2();
* $url = $oauthClient->buildAuthUrl(); // Build authorization URL
* Yii::$app->getResponse()->redirect($url); // Redirect to authorization URL.
* // After user returns at our site:
* $code = $_GET['code'];
* $accessToken = $oauthClient->fetchAccessToken($code); // Get access token
* ~~~
*
* @see http://oauth.net/2/
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class OAuth2 extends BaseOAuth
{
/**
* @var string protocol version.
*/
public $version = '2.0';
/**
* @var string OAuth client ID.
*/
public $clientId;
/**
* @var string OAuth client secret.
*/
public $clientSecret;
/**
* @var string token request URL endpoint.
*/
public $tokenUrl;
/**
* Composes user authorization URL.
* @param array $params additional auth GET params.
* @return string authorization URL.
*/
public function buildAuthUrl(array $params = [])
{
$defaultParams = [
'client_id' => $this->clientId,
'response_type' => 'code',
'redirect_uri' => $this->getReturnUrl(),
'xoauth_displayname' => Yii::$app->name,
];
if (!empty($this->scope)) {
$defaultParams['scope'] = $this->scope;
}
return $this->composeUrl($this->authUrl, array_merge($defaultParams, $params));
}
/**
* Fetches access token from authorization code.
* @param string $authCode authorization code, usually comes at $_GET['code'].
* @param array $params additional request params.
* @return OAuthToken access token.
*/
public function fetchAccessToken($authCode, array $params = [])
{
$defaultParams = [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'code' => $authCode,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->getReturnUrl(),
];
$response = $this->sendRequest('POST', $this->tokenUrl, array_merge($defaultParams, $params));
$token = $this->createToken(['params' => $response]);
$this->setAccessToken($token);
return $token;
}
/**
* Composes HTTP request CUrl options, which will be merged with the default ones.
* @param string $method request type.
* @param string $url request URL.
* @param array $params request params.
* @return array CUrl options.
* @throws Exception on failure.
*/
protected function composeRequestCurlOptions($method, $url, array $params)
{
$curlOptions = [];
switch ($method) {
case 'GET': {
$curlOptions[CURLOPT_URL] = $this->composeUrl($url, $params);
break;
}
case 'POST': {
$curlOptions[CURLOPT_POST] = true;
$curlOptions[CURLOPT_HTTPHEADER] = ['Content-type: application/x-www-form-urlencoded'];
$curlOptions[CURLOPT_POSTFIELDS] = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
break;
}
case 'HEAD':
case 'PUT':
case 'DELETE': {
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
if (!empty($params)) {
$curlOptions[CURLOPT_URL] = $this->composeUrl($url, $params);
}
break;
}
default: {
throw new Exception("Unknown request method '{$method}'.");
}
}
return $curlOptions;
}
/**
* Performs request to the OAuth API.
* @param OAuthToken $accessToken actual access token.
* @param string $url absolute API URL.
* @param string $method request method.
* @param array $params request parameters.
* @return array API response.
* @throws Exception on failure.
*/
protected function apiInternal($accessToken, $url, $method, array $params)
{
$params['access_token'] = $accessToken->getToken();
return $this->sendRequest($method, $url, $params);
}
/**
* Gets new auth token to replace expired one.
* @param OAuthToken $token expired auth token.
* @return OAuthToken new auth token.
*/
public function refreshAccessToken(OAuthToken $token)
{
$params = [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'grant_type' => 'refresh_token'
];
$params = array_merge($token->getParams(), $params);
$response = $this->sendRequest('POST', $this->tokenUrl, $params);
return $response;
}
/**
* Composes default {@link returnUrl} value.
* @return string return URL.
*/
protected function defaultReturnUrl()
{
$params = $_GET;
unset($params['code']);
return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params);
}
/**
* Creates token from its configuration.
* @param array $tokenConfig token configuration.
* @return OAuthToken token instance.
*/
protected function createToken(array $tokenConfig = [])
{
$tokenConfig['tokenParamKey'] = 'access_token';
return parent::createToken($tokenConfig);
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient;
use yii\base\Object;
/**
* Token represents OAuth token.
*
* @property array $params token parameters.
* @property string $token token value.
* @property string $tokenSecret token secret value.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class OAuthToken extends Object
{
/**
* @var string key in {@link _params} array, which stores token key.
*/
public $tokenParamKey = 'oauth_token';
/**
* @var string key in {@link _params} array, which stores token secret key.
*/
public $tokenSecretParamKey = 'oauth_token_secret';
/**
* @var string key in {@link _params} array, which stores token expiration duration.
* If not set will attempt to fetch its value automatically.
*/
private $_expireDurationParamKey;
/**
* @var array token parameters.
*/
private $_params = [];
/**
* @var integer object creation timestamp.
*/
public $createTimestamp;
public function init()
{
if ($this->createTimestamp === null) {
$this->createTimestamp = time();
}
}
/**
* @param string $expireDurationParamKey expire duration param key.
*/
public function setExpireDurationParamKey($expireDurationParamKey) {
$this->_expireDurationParamKey = $expireDurationParamKey;
}
/**
* @return string expire duration param key.
*/
public function getExpireDurationParamKey() {
if ($this->_expireDurationParamKey === null) {
$this->_expireDurationParamKey = $this->defaultExpireDurationParamKey();
}
return $this->_expireDurationParamKey;
}
/**
* @return array
*/
public function getParams() {
return $this->_params;
}
/**
* @param array $params
*/
public function setParams(array $params) {
$this->_params = $params;
}
/**
* Sets param by name.
* @param string $name param name.
* @param mixed $value param value,
*/
public function setParam($name, $value) {
$this->_params[$name] = $value;
}
/**
* Returns param by name.
* @param string $name param name.
* @return mixed param value.
*/
public function getParam($name) {
return isset($this->_params[$name]) ? $this->_params[$name] : null;
}
/**
* Sets token value.
* @param string $token token value.
* @return static self reference.
*/
public function setToken($token) {
$this->setParam($this->tokenParamKey, $token);
}
/**
* Returns token value.
* @return string token value.
*/
public function getToken() {
return $this->getParam($this->tokenParamKey);
}
/**
* Sets the token secret value.
* @param string $tokenSecret token secret.
*/
public function setTokenSecret($tokenSecret) {
$this->setParam($this->tokenSecretParamKey, $tokenSecret);
}
/**
* Returns the token secret value.
* @return string token secret value.
*/
public function getTokenSecret() {
return $this->getParam($this->tokenSecretParamKey);
}
/**
* Sets token expire duration.
* @param string $expireDuration token expiration duration.
*/
public function setExpireDuration($expireDuration) {
$this->setParam($this->getExpireDurationParamKey(), $expireDuration);
}
/**
* Returns the token expiration duration.
* @return integer token expiration duration.
*/
public function getExpireDuration() {
return $this->getParam($this->getExpireDurationParamKey());
}
/**
* Fetches default expire duration param key.
* @return string expire duration param key.
*/
protected function defaultExpireDurationParamKey() {
$expireDurationParamKey = 'expires_in';
foreach ($this->getParams() as $name => $value) {
if (strpos($name, 'expir') !== false) {
$expireDurationParamKey = $name;
break;
}
}
return $expireDurationParamKey;
}
/**
* Checks if token has expired.
* @return boolean is token expired.
*/
public function getIsExpired() {
$expirationDuration = $this->getExpireDuration();
if (empty($expirationDuration)) {
return false;
}
return (time() >= ($this->createTimestamp + $expirationDuration));
}
/**
* Checks if token is valid.
* @return boolean is token valid.
*/
public function getIsValid() {
$token = $this->getToken();
return (!empty($token) && !$this->getIsExpired());
}
}
\ No newline at end of file
AuthClient Extension for Yii 2
==============================
This extension adds [OpenID](http://openid.net/), [OAuth](http://oauth.net/) and [OAuth2](http://oauth.net/2/) consumers for the Yii 2 framework.
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require yiisoft/yii2-authclient "*"
```
or add
```json
"yiisoft/yii2-authclient": "*"
```
to the require section of your composer.json.
Usage & Documentation
---------------------
This extension provides the ability of the authentication via external credentials providers.
It covers OpenID, OAuth1 and OAuth2 protocols.
You need to setup auth client collection application component:
```
'components' => [
'authClientCollection' => [
'class' => 'yii\authclient\Collection',
'clients' => [
'google' => [
'class' => 'yii\authclient\clients\GoogleOpenId'
],
'facebook' => [
'class' => 'yii\authclient\clients\Facebook',
'clientId' => 'facebook_client_id',
'clientSecret' => 'facebook_client_secret',
],
],
]
...
]
```
Then you need to apply [[yii\authclient\AuthAction]] to some of your web controllers:
```
class SiteController extends Controller
{
public function actions()
{
return [
'auth' => [
'class' => 'yii\authclient\AuthAction',
'successCallback' => [$this, 'successCallback'],
],
]
}
public function successCallback($client)
{
$atributes = $client->getUserAttributes();
// user login or signup comes here
}
}
```
You may use [[yii\authclient\widgets\Choice]] to compose auth client selection:
```
<?= yii\authclient\Choice::widget([
'baseAuthUrl' => ['site/auth']
]); ?>
```
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OAuth2;
/**
* Facebook allows authentication via Facebook OAuth.
* In order to use Facebook OAuth you must register your application at [[https://developers.facebook.com/apps]].
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'facebook' => [
* 'class' => 'yii\authclient\clients\Facebook',
* 'clientId' => 'facebook_client_id',
* 'clientSecret' => 'facebook_client_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @see https://developers.facebook.com/apps
* @see http://developers.facebook.com/docs/reference/api
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Facebook extends OAuth2
{
/**
* @inheritdoc
*/
public $authUrl = 'https://www.facebook.com/dialog/oauth';
/**
* @inheritdoc
*/
public $tokenUrl = 'https://graph.facebook.com/oauth/access_token';
/**
* @inheritdoc
*/
public $apiBaseUrl = 'https://graph.facebook.com';
/**
* @inheritdoc
*/
public $scope = 'email';
/**
* @inheritdoc
*/
protected function initUserAttributes()
{
return $this->api('me', 'GET');
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'facebook';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'Facebook';
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OAuth2;
/**
* GitHub allows authentication via GitHub OAuth.
* In order to use GitHub OAuth you must register your application at [[https://github.com/settings/applications/new]].
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'github' => [
* 'class' => 'yii\authclient\clients\GitHub',
* 'clientId' => 'github_client_id',
* 'clientSecret' => 'github_client_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @see http://developer.github.com/v3/oauth/
* @see https://github.com/settings/applications/new
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class GitHub extends OAuth2
{
/**
* @inheritdoc
*/
public $authUrl = 'https://github.com/login/oauth/authorize';
/**
* @inheritdoc
*/
public $tokenUrl = 'https://github.com/login/oauth/access_token';
/**
* @inheritdoc
*/
public $apiBaseUrl = 'https://api.github.com';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->scope === null) {
$this->scope = implode(' ', [
'user',
'user:email',
]);
}
}
/**
* @inheritdoc
*/
protected function initUserAttributes()
{
return $this->api('user', 'GET');
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OAuth2;
/**
* GoogleOAuth allows authentication via Google OAuth.
* In order to use Google OAuth you must register your application at [[https://code.google.com/apis/console#access]].
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'google' => [
* 'class' => 'yii\authclient\clients\GoogleOAuth',
* 'clientId' => 'google_client_id',
* 'clientSecret' => 'google_client_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @see https://code.google.com/apis/console#access
* @see https://developers.google.com/google-apps/contacts/v3/
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class GoogleOAuth extends OAuth2
{
/**
* @inheritdoc
*/
public $authUrl = 'https://accounts.google.com/o/oauth2/auth';
/**
* @inheritdoc
*/
public $tokenUrl = 'https://accounts.google.com/o/oauth2/token';
/**
* @inheritdoc
*/
public $apiBaseUrl = 'https://www.googleapis.com/oauth2/v1';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->scope === null) {
$this->scope = implode(' ', [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
]);
}
}
/**
* @inheritdoc
*/
protected function initUserAttributes()
{
return $this->api('userinfo', 'GET');
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'google';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'Google';
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OpenId;
/**
* GoogleOpenId allows authentication via Google OpenId.
* Unlike Google OAuth you do not need to register your application anywhere in order to use Google OpenId.
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'google' => [
* 'class' => 'yii\authclient\clients\GoogleOpenId'
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class GoogleOpenId extends OpenId
{
/**
* @inheritdoc
*/
public $authUrl = 'https://www.google.com/accounts/o8/id';
/**
* @inheritdoc
*/
public $requiredAttributes = [
'namePerson/first',
'namePerson/last',
'contact/email',
'pref/language',
];
/**
* @inheritdoc
*/
protected function defaultNormalizeUserAttributeMap()
{
return [
'first_name' => 'namePerson/first',
'last_name' => 'namePerson/last',
'email' => 'contact/email',
'language' => 'pref/language',
];
}
/**
* @inheritdoc
*/
protected function defaultViewOptions()
{
return [
'popupWidth' => 880,
'popupHeight' => 520,
];
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'google';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'Google';
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OAuth2;
use yii\web\HttpException;
use Yii;
/**
* LinkedIn allows authentication via LinkedIn OAuth.
* In order to use linkedIn OAuth you must register your application at [[https://www.linkedin.com/secure/developer]].
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'linkedin' => [
* 'class' => 'yii\authclient\clients\LinkedIn',
* 'clientId' => 'linkedin_client_id',
* 'clientSecret' => 'linkedin_client_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @see http://developer.linkedin.com/documents/authentication
* @see https://www.linkedin.com/secure/developer
* @see http://developer.linkedin.com/apis
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class LinkedIn extends OAuth2
{
/**
* @inheritdoc
*/
public $authUrl = 'https://www.linkedin.com/uas/oauth2/authorization';
/**
* @inheritdoc
*/
public $tokenUrl = 'https://www.linkedin.com/uas/oauth2/accessToken';
/**
* @inheritdoc
*/
public $apiBaseUrl = 'https://api.linkedin.com/v1';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->scope === null) {
$this->scope = implode(' ', [
'r_basicprofile',
'r_emailaddress',
]);
}
}
/**
* @inheritdoc
*/
protected function defaultNormalizeUserAttributeMap()
{
return [
'email' => 'email-address',
'first_name' => 'first-name',
'last_name' => 'last-name',
];
}
/**
* @inheritdoc
*/
protected function initUserAttributes()
{
$attributeNames = [
'id',
'email-address',
'first-name',
'last-name',
'public-profile-url',
];
return $this->api('people/~:(' . implode(',', $attributeNames) . ')', 'GET');
}
/**
* @inheritdoc
*/
public function buildAuthUrl(array $params = [])
{
$authState = $this->generateAuthState();
$this->setState('authState', $authState);
$params['state'] = $authState;
return parent::buildAuthUrl($params);
}
/**
* @inheritdoc
*/
public function fetchAccessToken($authCode, array $params = [])
{
$authState = $this->getState('authState');
if (!isset($_REQUEST['state']) || empty($authState) || strcmp($_REQUEST['state'], $authState) !== 0) {
throw new HttpException(400, 'Invalid auth state parameter.');
} else {
$this->removeState('authState');
}
return parent::fetchAccessToken($authCode, $params);
}
/**
* @inheritdoc
*/
protected function apiInternal($accessToken, $url, $method, array $params)
{
$params['oauth2_access_token'] = $accessToken->getToken();
return $this->sendRequest($method, $url, $params);
}
/**
* @inheritdoc
*/
protected function defaultReturnUrl()
{
$params = $_GET;
unset($params['code']);
unset($params['state']);
return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params);
}
/**
* Generates the auth state value.
* @return string auth state value.
*/
protected function generateAuthState() {
return sha1(uniqid(get_class($this), true));
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'linkedin';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'LinkedIn';
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OAuth1;
/**
* Twitter allows authentication via Twitter OAuth.
* In order to use Twitter OAuth you must register your application at [[https://dev.twitter.com/apps/new]].
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'twitter' => [
* 'class' => 'yii\authclient\clients\Twitter',
* 'consumerKey' => 'twitter_consumer_key',
* 'consumerSecret' => 'twitter_consumer_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @see https://dev.twitter.com/apps/new
* @see https://dev.twitter.com/docs/api
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Twitter extends OAuth1
{
/**
* @inheritdoc
*/
public $authUrl = 'https://api.twitter.com/oauth/authorize';
/**
* @inheritdoc
*/
public $requestTokenUrl = 'https://api.twitter.com/oauth/request_token';
/**
* @inheritdoc
*/
public $requestTokenMethod = 'POST';
/**
* @inheritdoc
*/
public $accessTokenUrl = 'https://api.twitter.com/oauth/access_token';
/**
* @inheritdoc
*/
public $accessTokenMethod = 'POST';
/**
* @inheritdoc
*/
public $apiBaseUrl = 'https://api.twitter.com/1.1';
/**
* @inheritdoc
*/
protected function initUserAttributes()
{
return $this->api('account/verify_credentials.json', 'GET');
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'twitter';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'Twitter';
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OAuth2;
/**
* YandexOAuth allows authentication via Yandex OAuth.
* In order to use Yandex OAuth you must register your application at [[https://oauth.yandex.ru/client/new]].
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'yandex' => [
* 'class' => 'yii\authclient\clients\YandexOAuth',
* 'clientId' => 'yandex_client_id',
* 'clientSecret' => 'yandex_client_secret',
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @see https://oauth.yandex.ru/client/new
* @see http://api.yandex.ru/login/doc/dg/reference/response.xml
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class YandexOAuth extends OAuth2
{
/**
* @inheritdoc
*/
public $authUrl = 'https://oauth.yandex.ru/authorize';
/**
* @inheritdoc
*/
public $tokenUrl = 'https://oauth.yandex.ru/token';
/**
* @inheritdoc
*/
public $apiBaseUrl = 'https://login.yandex.ru';
/**
* @inheritdoc
*/
protected function initUserAttributes()
{
return $this->api('info', 'GET');
}
/**
* @inheritdoc
*/
protected function apiInternal($accessToken, $url, $method, array $params)
{
if (!isset($params['format'])) {
$params['format'] = 'json';
}
$params['oauth_token'] = $accessToken->getToken();
return $this->sendRequest($method, $url, $params);
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'yandex';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'Yandex';
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\clients;
use yii\authclient\OpenId;
/**
* YandexOpenId allows authentication via Yandex OpenId.
* Unlike Yandex OAuth you do not need to register your application anywhere in order to use Yandex OpenId.
*
* Example application configuration:
*
* ~~~
* 'components' => [
* 'authClientCollection' => [
* 'class' => 'yii\authclient\Collection',
* 'clients' => [
* 'yandex' => [
* 'class' => 'yii\authclient\clients\YandexOpenId'
* ],
* ],
* ]
* ...
* ]
* ~~~
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class YandexOpenId extends OpenId
{
/**
* @inheritdoc
*/
public $authUrl = 'http://openid.yandex.ru';
/**
* @inheritdoc
*/
public $requiredAttributes = [
'namePerson',
'contact/email',
];
/**
* @inheritdoc
*/
protected function defaultNormalizeUserAttributeMap()
{
return [
'name' => 'namePerson',
'email' => 'contact/email',
];
}
/**
* @inheritdoc
*/
protected function defaultViewOptions()
{
return [
'popupWidth' => 900,
'popupHeight' => 550,
];
}
/**
* @inheritdoc
*/
protected function defaultName()
{
return 'yandex';
}
/**
* @inheritdoc
*/
protected function defaultTitle()
{
return 'Yandex';
}
}
\ No newline at end of file
{
"name": "yiisoft/yii2-authclient",
"description": "External authentication via OAuth and OpenID for the Yii framework",
"keywords": ["yii", "OAuth", "OpenID", "auth"],
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"authors": [
{
"name": "Paul Klimov",
"email": "klimov.paul@gmail.com"
}
],
"require": {
"yiisoft/yii2": "*",
"ext-curl": "*"
},
"autoload": {
"psr-0": { "yii\\authclient\\": "" }
},
"target-dir": "yii/authclient"
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\signature;
use yii\base\Object;
/**
* BaseMethod is a base class for the OAuth signature methods.
*
* @property string $name method canonical name. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
abstract class BaseMethod extends Object
{
/**
* Return the canonical name of the Signature Method.
* @return string method name.
*/
abstract public function getName();
/**
* Generates OAuth request signature.
* @param string $baseString signature base string.
* @param string $key signature key.
* @return string signature string.
*/
abstract public function generateSignature($baseString, $key);
/**
* Verifies given OAuth request.
* @param string $signature signature to be verified.
* @param string $baseString signature base string.
* @param string $key signature key.
* @return boolean success.
*/
public function verify($signature, $baseString, $key)
{
$expectedSignature = $this->generateSignature($baseString, $key);
if (empty($signature) || empty($expectedSignature)) {
return false;
}
return (strcmp($expectedSignature, $signature) === 0);
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\signature;
use yii\base\NotSupportedException;
/**
* HmacSha1 represents 'HMAC-SHA1' signature method.
*
* Note: This class require PHP "Hash" extension({@link http://php.net/manual/en/book.hash.php}).
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class HmacSha1 extends BaseMethod
{
/**
* @inheritdoc
*/
public function init()
{
if (!function_exists('hash_hmac')) {
throw new NotSupportedException('PHP "Hash" extension is required.');
}
}
/**
* @inheritdoc
*/
public function getName()
{
return 'HMAC-SHA1';
}
/**
* @inheritdoc
*/
public function generateSignature($baseString, $key)
{
return base64_encode(hash_hmac('sha1', $baseString, $key, true));
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\signature;
/**
* PlainText represents 'PLAINTEXT' signature method.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class PlainText extends BaseMethod
{
/**
* @inheritdoc
*/
public function getName()
{
return 'PLAINTEXT';
}
/**
* @inheritdoc
*/
public function generateSignature($baseString, $key)
{
return $key;
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\signature;
use yii\base\InvalidConfigException;
use yii\base\NotSupportedException;
/**
* RsaSha1 represents 'RSA-SHA1' signature method.
*
* Note: This class require PHP "OpenSSL" extension({@link http://php.net/manual/en/book.openssl.php}).
*
* @property string $privateCertificate OpenSSL private key certificate content.
* @property string $publicCertificate OpenSSL public key certificate content.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class RsaSha1 extends BaseMethod
{
/**
* @var string OpenSSL private key certificate content.
* This value can be fetched from file specified by {@link privateCertificateFile}.
*/
protected $_privateCertificate;
/**
* @var string OpenSSL public key certificate content.
* This value can be fetched from file specified by {@link publicCertificateFile}.
*/
protected $_publicCertificate;
/**
* @var string path to the file, which holds private key certificate.
*/
public $privateCertificateFile = '';
/**
* @var string path to the file, which holds public key certificate.
*/
public $publicCertificateFile = '';
/**
* @inheritdoc
*/
public function init()
{
if (!function_exists('openssl_sign')) {
throw new NotSupportedException('PHP "OpenSSL" extension is required.');
}
}
/**
* @param string $publicCertificate public key certificate content.
*/
public function setPublicCertificate($publicCertificate)
{
$this->_publicCertificate = $publicCertificate;
}
/**
* @return string public key certificate content.
*/
public function getPublicCertificate()
{
if ($this->_publicCertificate === null) {
$this->_publicCertificate = $this->initPublicCertificate();
}
return $this->_publicCertificate;
}
/**
* @param string $privateCertificate private key certificate content.
*/
public function setPrivateCertificate($privateCertificate)
{
$this->_privateCertificate = $privateCertificate;
}
/**
* @return string private key certificate content.
*/
public function getPrivateCertificate()
{
if ($this->_privateCertificate === null) {
$this->_privateCertificate = $this->initPrivateCertificate();
}
return $this->_privateCertificate;
}
/**
* @inheritdoc
*/
public function getName()
{
return 'RSA-SHA1';
}
/**
* Creates initial value for {@link publicCertificate}.
* This method will attempt to fetch the certificate value from {@link publicCertificateFile} file.
* @throws InvalidConfigException on failure.
* @return string public certificate content.
*/
protected function initPublicCertificate()
{
if (!empty($this->publicCertificateFile)) {
if (!file_exists($this->publicCertificateFile)) {
throw new InvalidConfigException("Public certificate file '{$this->publicCertificateFile}' does not exist!");
}
return file_get_contents($this->publicCertificateFile);
} else {
return '';
}
}
/**
* Creates initial value for {@link privateCertificate}.
* This method will attempt to fetch the certificate value from {@link privateCertificateFile} file.
* @throws InvalidConfigException on failure.
* @return string private certificate content.
*/
protected function initPrivateCertificate()
{
if (!empty($this->privateCertificateFile)) {
if (!file_exists($this->privateCertificateFile)) {
throw new InvalidConfigException("Private certificate file '{$this->privateCertificateFile}' does not exist!");
}
return file_get_contents($this->privateCertificateFile);
} else {
return '';
}
}
/**
* @inheritdoc
*/
public function generateSignature($baseString, $key)
{
$privateCertificateContent = $this->getPrivateCertificate();
// Pull the private key ID from the certificate
$privateKeyId = openssl_pkey_get_private($privateCertificateContent);
// Sign using the key
openssl_sign($baseString, $signature, $privateKeyId);
// Release the key resource
openssl_free_key($privateKeyId);
return base64_encode($signature);
}
/**
* @inheritdoc
*/
public function verify($signature, $baseString, $key)
{
$decodedSignature = base64_decode($signature);
// Fetch the public key cert based on the request
$publicCertificate = $this->getPublicCertificate();
// Pull the public key ID from the certificate
$publicKeyId = openssl_pkey_get_public($publicCertificate);
// Check the computed signature against the one passed in the query
$verificationResult = openssl_verify($baseString, $decodedSignature, $publicKeyId);
// Release the key resource
openssl_free_key($publicKeyId);
return ($verificationResult == 1);
}
}
\ No newline at end of file
<?php
use yii\helpers\Html;
use yii\helpers\Json;
/* @var $this \yii\base\View */
/* @var $url string */
/* @var $enforceRedirect boolean */
$redirectJavaScript = <<<EOL
function popupWindowRedirect(url, enforceRedirect = true) {
if (window.opener) {
window.close();
if (enforceRedirect) {
window.opener.location = url;
}
} else {
window.location = url;
}
}
EOL;
$redirectJavaScript .= 'popupWindowRedirect(' . Json::encode($url) . ', ' . Json::encode($enforceRedirect) . ');';
?>
<!DOCTYPE html>
<html>
<head>
<?= Html::script($redirectJavaScript); ?>
</head>
<body>
<h2 id="title" style="display:none;">Redirecting back to the &quot;<?= Yii::$app->name; ?>&quot;...</h2>
<h3 id="link"><a href="<?= $url; ?>">Click here to return to the &quot;<?= Yii::$app->name; ?>&quot;.</a></h3>
<script type="text/javascript">
document.getElementById('title').style.display = '';
document.getElementById('link').style.display = 'none';
</script>
</body>
</html>
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\widgets;
use yii\base\Widget;
use Yii;
use yii\helpers\Html;
use yii\authclient\ClientInterface;
/**
* Choice prints buttons for authentication via various auth clients.
* By default this widget relies on presence of [[\yii\authclient\Collection]] among application components
* to get auth clients information.
*
* Example:
* ~~~
* <?= yii\authclient\Choice::widget([
* 'baseAuthUrl' => ['site/auth']
* ]); ?>
* ~~~
*
* You can customize the widget appearance by using [[beginWidget()]] and [[endWidget()]] syntax
* along with using method {@link clientLink()} or {@link createClientUrl()}.
* For example:
*
* ~~~
* <?php $authChoice = yii\authclient\Choice::beginWidget([
* 'baseAuthUrl' => ['site/auth']
* ]); ?>
* <ul>
* <?php foreach ($authChoice->getClients() as $client): ?>
* <li><?= $authChoice->clientLink($client); ?></li>
* <?php endforeach; ?>
* </ul>
* <?php yii\authclient\Choice::endWidget(); ?>
* ~~~
*
* @see \yii\authclient\AuthAction
*
* @property ClientInterface[] $providers auth providers list.
* @property array $baseAuthUrl configuration for the external services base authentication URL.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Choice extends Widget
{
/**
* @var ClientInterface[] auth providers list.
*/
private $_clients;
/**
* @var string name of the auth client collection application component.
* This component will be used to fetch {@link services} value if it is not set.
*/
public $clientCollection = 'authClientCollection';
/**
* @var array configuration for the external clients base authentication URL.
*/
private $_baseAuthUrl;
/**
* @var string name of the GET param , which should be used to passed auth client id to URL
* defined by {@link baseAuthUrl}.
*/
public $clientIdGetParamName = 'authclient';
/**
* @var array the HTML attributes that should be rendered in the div HTML tag representing the container element.
*/
public $mainContainerHtmlOptions = [
'class' => 'auth-clients'
];
/**
* @var boolean indicates if popup window should be used instead of direct links.
*/
public $popupMode = true;
/**
* @var boolean indicates if widget content, should be rendered automatically.
* Note: this value automatically set to 'false' at the first call of [[createProviderUrl()]]
*/
public $autoRender = true;
/**
* @param ClientInterface[] $clients auth providers
*/
public function setClients(array $clients)
{
$this->_clients = $clients;
}
/**
* @return ClientInterface[] auth providers
*/
public function getClients()
{
if ($this->_clients === null) {
$this->_clients = $this->defaultClients();
}
return $this->_clients;
}
/**
* @param array $baseAuthUrl base auth URL configuration.
*/
public function setBaseAuthUrl(array $baseAuthUrl)
{
$this->_baseAuthUrl = $baseAuthUrl;
}
/**
* @return array base auth URL configuration.
*/
public function getBaseAuthUrl()
{
if (!is_array($this->_baseAuthUrl)) {
$this->_baseAuthUrl = $this->defaultBaseAuthUrl();
}
return $this->_baseAuthUrl;
}
/**
* Returns default auth clients list.
* @return ClientInterface[] auth clients list.
*/
protected function defaultClients()
{
/** @var $collection \yii\authclient\Collection */
$collection = Yii::$app->getComponent($this->clientCollection);
return $collection->getClients();
}
/**
* Composes default base auth URL configuration.
* @return array base auth URL configuration.
*/
protected function defaultBaseAuthUrl()
{
$baseAuthUrl = [
Yii::$app->controller->getRoute()
];
$params = $_GET;
unset($params[$this->clientIdGetParamName]);
$baseAuthUrl = array_merge($baseAuthUrl, $params);
return $baseAuthUrl;
}
/**
* Outputs client auth link.
* @param ClientInterface $client external auth client instance.
* @param string $text link text, if not set - default value will be generated.
* @param array $htmlOptions link HTML options.
*/
public function clientLink($client, $text = null, array $htmlOptions = [])
{
if ($text === null) {
$text = Html::tag('span', '', ['class' => 'auth-icon ' . $client->getName()]);
$text .= Html::tag('span', $client->getTitle(), ['class' => 'auth-title']);
}
if (!array_key_exists('class', $htmlOptions)) {
$htmlOptions['class'] = 'auth-link ' . $client->getName();
}
if ($this->popupMode) {
$viewOptions = $client->getViewOptions();
if (isset($viewOptions['popupWidth'])) {
$htmlOptions['data-popup-width'] = $viewOptions['popupWidth'];
}
if (isset($viewOptions['popupHeight'])) {
$htmlOptions['data-popup-height'] = $viewOptions['popupHeight'];
}
}
echo Html::a($text, $this->createClientUrl($client), $htmlOptions);
}
/**
* Composes client auth URL.
* @param ClientInterface $provider external auth client instance.
* @return string auth URL.
*/
public function createClientUrl($provider)
{
$this->autoRender = false;
$url = $this->getBaseAuthUrl();
$url[$this->clientIdGetParamName] = $provider->getId();
return Html::url($url);
}
/**
* Renders the main content, which includes all external services links.
*/
protected function renderMainContent()
{
echo Html::beginTag('ul', ['class' => 'auth-clients clear']);
foreach ($this->getClients() as $externalService) {
echo Html::beginTag('li', ['class' => 'auth-client']);
$this->clientLink($externalService);
echo Html::endTag('li');
}
echo Html::endTag('ul');
}
/**
* Initializes the widget.
*/
public function init()
{
if ($this->popupMode) {
$view = Yii::$app->getView();
ChoiceAsset::register($view);
$view->registerJs("\$('#" . $this->getId() . "').authchoice();");
}
$this->mainContainerHtmlOptions['id'] = $this->getId();
echo Html::beginTag('div', $this->mainContainerHtmlOptions);
}
/**
* Runs the widget.
*/
public function run()
{
if ($this->autoRender) {
$this->renderMainContent();
}
echo Html::endTag('div');
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\authclient\widgets;
use yii\web\AssetBundle;
/**
* ChoiceAsset is an asset bundle for [[Choice]] widget.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class ChoiceAsset extends AssetBundle
{
public $sourcePath = '@yii/authclient/widgets/assets';
public $js = [
'authchoice.js',
];
public $css = [
'authchoice.css',
];
public $depends = [
'yii\web\YiiAsset',
];
}
\ No newline at end of file
.clients {
overflow:auto;
}
.auth-icon {
display: block;
width: 32px;
height: 32px;
background: url(authchoice.png) no-repeat;
}
.auth-icon.google,
.auth-icon.google_openid,
.auth-icon.google_oauth {
background-position: 0 -34px;
}
.auth-icon.twitter {
background-position: 0 -68px;
}
.auth-icon.yandex,
.auth-icon.yandex_openid,
.auth-icon.yandex_oauth {
background-position: 0 -102px;
}
.auth-icon.vkontakte {
background-position: 0 -136px;
}
.auth-icon.facebook {
background-position: 0 -170px;
}
.auth-icon.mailru {
background-position: 0 -204px;
}
.auth-icon.moikrug {
background-position: 0 -238px;
}
.auth-icon.odnoklassniki {
background-position: 0 -272px;
}
.auth-icon.linkedin {
background-position: 0 -306px;
}
.auth-icon.github {
background-position: 0 -340px;
}
.auth-icon.live {
background-position: 0 -372px;
}
.auth-link:hover .auth-icon i,
.auth-link:focus .auth-icon i {
display: block;
width: 32px;
height: 32px;
background: url(authchoice.png) 0 0 no-repeat;
}
.auth-clients {
margin: 0 0 1em;
list-style: none;
overflow: auto;
}
.auth-client {
float: left;
margin: 0 1em 0 0;
}
.auth-clients .auth-client .auth-link {
display: block;
width: 58px;
}
.auth-client .auth-link .auth-icon {
margin: 0 auto;
}
.auth-client .auth-link .auth-title {
display: block;
margin-top: 0.4em;
text-align: center;
}
\ No newline at end of file
/**
* Yii auth choice widget.
*
* This is the JavaScript widget used by the yii\authclient\widgets\Choice widget.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
jQuery(function($) {
$.fn.authchoice = function(options) {
options = $.extend({
popup: {
resizable: 'yes',
scrollbars: 'no',
toolbar: 'no',
menubar: 'no',
location: 'no',
directories: 'no',
status: 'yes',
width: 450,
height: 380
}
}, options);
return this.each(function() {
var $container = $(this);
$container.find('a').on('click', function(e) {
e.preventDefault();
var authChoicePopup = null;
if (authChoicePopup = $container.data('authChoicePopup')) {
authChoicePopup.close();
}
var url = this.href;
var popupOptions = options.popup;
var localPopupWidth = this.getAttribute('data-popup-width');
if (localPopupWidth) {
popupOptions.width = localPopupWidth;
}
var localPopupHeight = this.getAttribute('data-popup-height');
if (localPopupWidth) {
popupOptions.height = localPopupHeight;
}
popupOptions.left = (window.screen.width - options.popup.width) / 2;
popupOptions.top = (window.screen.height - options.popup.height) / 2;
var popupFeatureParts = [];
for (var propName in popupOptions) {
popupFeatureParts.push(propName + '=' + popupOptions[propName]);
}
var popupFeature = popupFeatureParts.join(',');
authChoicePopup = window.open(url, 'yii_auth_choice', popupFeature);
authChoicePopup.focus();
$container.data('authChoicePopup', authChoicePopup);
});
});
};
});
<?php
namespace yiiunit\extensions\authclient;
use yii\authclient\AuthAction;
class AuthActionTest extends TestCase
{
protected function setUp()
{
$config = [
'components' => [
'user' => [
'identityClass' => '\yii\web\IdentityInterface'
],
'request' => [
'hostInfo' => 'http://testdomain.com',
'scriptUrl' => '/index.php',
],
]
];
$this->mockApplication($config, '\yii\web\Application');
}
public function testSetGet()
{
$action = new AuthAction(null, null);
$successUrl = 'http://test.success.url';
$action->setSuccessUrl($successUrl);
$this->assertEquals($successUrl, $action->getSuccessUrl(), 'Unable to setup success URL!');
$cancelUrl = 'http://test.cancel.url';
$action->setCancelUrl($cancelUrl);
$this->assertEquals($cancelUrl, $action->getCancelUrl(), 'Unable to setup cancel URL!');
}
/**
* @depends testSetGet
*/
public function testGetDefaultSuccessUrl()
{
$action = new AuthAction(null, null);
$this->assertNotEmpty($action->getSuccessUrl(), 'Unable to get default success URL!');
}
/**
* @depends testSetGet
*/
public function testGetDefaultCancelUrl()
{
$action = new AuthAction(null, null);
$this->assertNotEmpty($action->getSuccessUrl(), 'Unable to get default cancel URL!');
}
public function testRedirect()
{
$action = new AuthAction(null, null);
$url = 'http://test.url';
$response = $action->redirect($url, true);
$this->assertContains($url, $response->content);
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient;
use yii\authclient\BaseClient;
class BaseClientTest extends TestCase
{
public function testSetGet()
{
$client = new Client();
$id = 'test_id';
$client->setId($id);
$this->assertEquals($id, $client->getId(), 'Unable to setup id!');
$name = 'test_name';
$client->setName($name);
$this->assertEquals($name, $client->getName(), 'Unable to setup name!');
$title = 'test_title';
$client->setTitle($title);
$this->assertEquals($title, $client->getTitle(), 'Unable to setup title!');
$userAttributes = [
'attribute1' => 'value1',
'attribute2' => 'value2',
];
$client->setUserAttributes($userAttributes);
$this->assertEquals($userAttributes, $client->getUserAttributes(), 'Unable to setup user attributes!');
$normalizeUserAttributeMap = [
'name' => 'some/name',
'email' => 'some/email',
];
$client->setNormalizeUserAttributeMap($normalizeUserAttributeMap);
$this->assertEquals($normalizeUserAttributeMap, $client->getNormalizeUserAttributeMap(), 'Unable to setup normalize user attribute map!');
$viewOptions = [
'option1' => 'value1',
'option2' => 'value2',
];
$client->setViewOptions($viewOptions);
$this->assertEquals($viewOptions, $client->getViewOptions(), 'Unable to setup view options!');
}
public function testGetDefaults()
{
$client = new Client();
$this->assertNotEmpty($client->getName(), 'Unable to get default name!');
$this->assertNotEmpty($client->getTitle(), 'Unable to get default title!');
$this->assertNotNull($client->getViewOptions(), 'Unable to get default view options!');
$this->assertNotNull($client->getNormalizeUserAttributeMap(), 'Unable to get default normalize user attribute map!');
}
/**
* @depends testSetGet
*/
public function testNormalizeUserAttributes()
{
$client = new Client();
$normalizeUserAttributeMap = [
'raw/name' => 'name',
'raw/email' => 'email',
];
$client->setNormalizeUserAttributeMap($normalizeUserAttributeMap);
$rawUserAttributes = [
'raw/name' => 'name value',
'raw/email' => 'email value',
];
$client->setUserAttributes($rawUserAttributes);
$normalizedUserAttributes = $client->getUserAttributes();
$expectedNormalizedUserAttributes = array_combine(array_keys($normalizeUserAttributeMap), array_values($rawUserAttributes));
$this->assertEquals($expectedNormalizedUserAttributes, $normalizedUserAttributes);
}
}
class Client extends BaseClient
{
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient;
use yii\authclient\signature\PlainText;
use yii\authclient\OAuthToken;
use yiiunit\extensions\authclient\TestCase;
use yii\authclient\BaseOAuth;
class BaseOAuthTest extends TestCase
{
/**
* Creates test OAuth client instance.
* @return BaseOAuth oauth client.
*/
protected function createOAuthClient()
{
$oauthClient = $this->getMock(BaseOAuth::className(), ['setState', 'getState', 'composeRequestCurlOptions', 'refreshAccessToken', 'apiInternal']);
$oauthClient->expects($this->any())->method('setState')->will($this->returnValue($oauthClient));
$oauthClient->expects($this->any())->method('getState')->will($this->returnValue(null));
return $oauthClient;
}
/**
* Invokes the OAuth client method even if it is protected.
* @param BaseOAuth $oauthClient OAuth client instance.
* @param string $methodName name of the method to be invoked.
* @param array $arguments method arguments.
* @return mixed method invoke result.
*/
protected function invokeOAuthClientMethod($oauthClient, $methodName, array $arguments = [])
{
$classReflection = new \ReflectionClass(get_class($oauthClient));
$methodReflection = $classReflection->getMethod($methodName);
$methodReflection->setAccessible(true);
$result = $methodReflection->invokeArgs($oauthClient, $arguments);
$methodReflection->setAccessible(false);
return $result;
}
// Tests :
public function testSetGet()
{
$oauthClient = $this->createOAuthClient();
$returnUrl = 'http://test.return.url';
$oauthClient->setReturnUrl($returnUrl);
$this->assertEquals($returnUrl, $oauthClient->getReturnUrl(), 'Unable to setup return URL!');
$curlOptions = [
'option1' => 'value1',
'option2' => 'value2',
];
$oauthClient->setCurlOptions($curlOptions);
$this->assertEquals($curlOptions, $oauthClient->getCurlOptions(), 'Unable to setup cURL options!');
}
public function testSetupComponents()
{
$oauthClient = $this->createOAuthClient();
$oauthToken = new OAuthToken();
$oauthClient->setAccessToken($oauthToken);
$this->assertEquals($oauthToken, $oauthClient->getAccessToken(), 'Unable to setup token!');
$oauthSignatureMethod = new PlainText();
$oauthClient->setSignatureMethod($oauthSignatureMethod);
$this->assertEquals($oauthSignatureMethod, $oauthClient->getSignatureMethod(), 'Unable to setup signature method!');
}
/**
* @depends testSetupComponents
*/
public function testSetupComponentsByConfig()
{
$oauthClient = $this->createOAuthClient();
$oauthToken = [
'token' => 'test_token',
'tokenSecret' => 'test_token_secret',
];
$oauthClient->setAccessToken($oauthToken);
$this->assertEquals($oauthToken['token'], $oauthClient->getAccessToken()->getToken(), 'Unable to setup token as config!');
$oauthSignatureMethod = [
'class' => 'yii\authclient\signature\PlainText'
];
$oauthClient->setSignatureMethod($oauthSignatureMethod);
$returnedSignatureMethod = $oauthClient->getSignatureMethod();
$this->assertEquals($oauthSignatureMethod['class'], get_class($returnedSignatureMethod), 'Unable to setup signature method as config!');
}
/**
* Data provider for [[testComposeUrl()]].
* @return array test data.
*/
public function composeUrlDataProvider()
{
return [
[
'http://test.url',
[
'param1' => 'value1',
'param2' => 'value2',
],
'http://test.url?param1=value1&param2=value2',
],
[
'http://test.url?with=some',
[
'param1' => 'value1',
'param2' => 'value2',
],
'http://test.url?with=some&param1=value1&param2=value2',
],
];
}
/**
* @dataProvider composeUrlDataProvider
*
* @param string $url request URL.
* @param array $params request params
* @param string $expectedUrl expected composed URL.
*/
public function testComposeUrl($url, array $params, $expectedUrl)
{
$oauthClient = $this->createOAuthClient();
$composedUrl = $this->invokeOAuthClientMethod($oauthClient, 'composeUrl', [$url, $params]);
$this->assertEquals($expectedUrl, $composedUrl);
}
/**
* Data provider for {@link testDetermineContentTypeByHeaders}.
* @return array test data.
*/
public function determineContentTypeByHeadersDataProvider()
{
return [
[
['content_type' => 'application/json'],
'json'
],
[
['content_type' => 'application/x-www-form-urlencoded'],
'urlencoded'
],
[
['content_type' => 'application/xml'],
'xml'
],
[
['some_header' => 'some_header_value'],
'auto'
],
[
['content_type' => 'unknown'],
'auto'
],
];
}
/**
* @dataProvider determineContentTypeByHeadersDataProvider
*
* @param array $headers request headers.
* @param string $expectedResponseType expected response type.
*/
public function testDetermineContentTypeByHeaders(array $headers, $expectedResponseType)
{
$oauthClient = $this->createOAuthClient();
$responseType = $this->invokeOAuthClientMethod($oauthClient, 'determineContentTypeByHeaders', [$headers]);
$this->assertEquals($expectedResponseType, $responseType);
}
/**
* Data provider for [[testDetermineContentTypeByRaw]].
* @return array test data.
*/
public function determineContentTypeByRawDataProvider()
{
return array(
['{name: value}', 'json'],
['name=value', 'urlencoded'],
['name1=value1&name2=value2', 'urlencoded'],
['<?xml version="1.0" encoding="UTF-8"?><tag>Value</tag>', 'xml'],
['<tag>Value</tag>', 'xml'],
);
}
/**
* @dataProvider determineContentTypeByRawDataProvider
*
* @param string $rawResponse raw response content.
* @param string $expectedResponseType expected response type.
*/
public function testDetermineContentTypeByRaw($rawResponse, $expectedResponseType)
{
$oauthClient = $this->createOAuthClient();
$responseType = $this->invokeOAuthClientMethod($oauthClient, 'determineContentTypeByRaw', [$rawResponse]);
$this->assertEquals($expectedResponseType, $responseType);
}
/**
* Data provider for [[testApiUrl]].
* @return array test data.
*/
public function apiUrlDataProvider()
{
return [
[
'http://api.base.url',
'sub/url',
'http://api.base.url/sub/url',
],
[
'http://api.base.url',
'http://api.base.url/sub/url',
'http://api.base.url/sub/url',
],
[
'http://api.base.url',
'https://api.base.url/sub/url',
'https://api.base.url/sub/url',
],
];
}
/**
* @dataProvider apiUrlDataProvider
*
* @param $apiBaseUrl
* @param $apiSubUrl
* @param $expectedApiFullUrl
*/
public function testApiUrl($apiBaseUrl, $apiSubUrl, $expectedApiFullUrl)
{
$oauthClient = $this->createOAuthClient();
$oauthClient->expects($this->any())->method('apiInternal')->will($this->returnArgument(1));
$accessToken = new OAuthToken();
$accessToken->setToken('test_access_token');
$accessToken->setExpireDuration(1000);
$oauthClient->setAccessToken($accessToken);
$oauthClient->apiBaseUrl = $apiBaseUrl;
$this->assertEquals($expectedApiFullUrl, $oauthClient->api($apiSubUrl));
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient;
use yii\authclient\Collection;
use yii\authclient\BaseClient;
use yiiunit\extensions\authclient\TestCase;
class CollectionTest extends TestCase
{
// Tests :
public function testSetGet()
{
$collection = new Collection();
$clients = [
'testClient1' => new TestClient(),
'testClient2' => new TestClient(),
];
$collection->setClients($clients);
$this->assertEquals($clients, $collection->getClients(), 'Unable to setup clients!');
}
/**
* @depends testSetGet
*/
public function testGetProviderById()
{
$collection = new Collection();
$clientId = 'testClientId';
$client = new TestClient();
$clients = [
$clientId => $client
];
$collection->setClients($clients);
$this->assertEquals($client, $collection->getClient($clientId), 'Unable to get client by id!');
}
/**
* @depends testGetProviderById
*/
public function testCreateProvider()
{
$collection = new Collection();
$clientId = 'testClientId';
$clientClassName = TestClient::className();
$clients = [
$clientId => [
'class' => $clientClassName
]
];
$collection->setClients($clients);
$provider = $collection->getClient($clientId);
$this->assertTrue(is_object($provider), 'Unable to create client by config!');
$this->assertTrue(is_a($provider, $clientClassName), 'Client has wrong class name!');
}
/**
* @depends testSetGet
*/
public function testHasProvider()
{
$collection = new Collection();
$clientName = 'testClientName';
$clients = [
$clientName => [
'class' => 'TestClient1'
],
];
$collection->setClients($clients);
$this->assertTrue($collection->hasClient($clientName), 'Existing client check fails!');
$this->assertFalse($collection->hasClient('unExistingClientName'), 'Not existing client check fails!');
}
}
class TestClient extends BaseClient
{
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\oauth;
use yii\authclient\OAuth1;
use yii\authclient\signature\PlainText;
use yii\authclient\OAuthToken;
use yiiunit\extensions\authclient\TestCase;
class OAuth1Test extends TestCase
{
protected function setUp()
{
$this->mockApplication([], '\yii\web\Application');
}
/**
* Invokes the OAuth client method even if it is protected.
* @param OAuth1 $oauthClient OAuth client instance.
* @param string $methodName name of the method to be invoked.
* @param array $arguments method arguments.
* @return mixed method invoke result.
*/
protected function invokeOAuthClientMethod($oauthClient, $methodName, array $arguments = [])
{
$classReflection = new \ReflectionClass(get_class($oauthClient));
$methodReflection = $classReflection->getMethod($methodName);
$methodReflection->setAccessible(true);
$result = $methodReflection->invokeArgs($oauthClient, $arguments);
$methodReflection->setAccessible(false);
return $result;
}
// Tests :
public function testSignRequest()
{
$oauthClient = new OAuth1();
$oauthSignatureMethod = new PlainText();
$oauthClient->setSignatureMethod($oauthSignatureMethod);
$signedParams = $this->invokeOAuthClientMethod($oauthClient, 'signRequest', ['GET', 'http://test.url', []]);
$this->assertNotEmpty($signedParams['oauth_signature'], 'Unable to sign request!');
}
/**
* Data provider for [[testComposeAuthorizationHeader()]].
* @return array test data.
*/
public function composeAuthorizationHeaderDataProvider()
{
return [
[
'',
[
'oauth_test_name_1' => 'oauth_test_value_1',
'oauth_test_name_2' => 'oauth_test_value_2',
],
'Authorization: OAuth oauth_test_name_1="oauth_test_value_1", oauth_test_name_2="oauth_test_value_2"'
],
[
'test_realm',
[
'oauth_test_name_1' => 'oauth_test_value_1',
'oauth_test_name_2' => 'oauth_test_value_2',
],
'Authorization: OAuth realm="test_realm", oauth_test_name_1="oauth_test_value_1", oauth_test_name_2="oauth_test_value_2"'
],
[
'',
[
'oauth_test_name_1' => 'oauth_test_value_1',
'test_name_2' => 'test_value_2',
],
'Authorization: OAuth oauth_test_name_1="oauth_test_value_1"'
],
];
}
/**
* @dataProvider composeAuthorizationHeaderDataProvider
*
* @param string $realm authorization realm.
* @param array $params request params.
* @param string $expectedAuthorizationHeader expected authorization header.
*/
public function testComposeAuthorizationHeader($realm, array $params, $expectedAuthorizationHeader)
{
$oauthClient = new OAuth1();
$authorizationHeader = $this->invokeOAuthClientMethod($oauthClient, 'composeAuthorizationHeader', [$params, $realm]);
$this->assertEquals($expectedAuthorizationHeader, $authorizationHeader);
}
public function testBuildAuthUrl() {
$oauthClient = new OAuth1();
$authUrl = 'http://test.auth.url';
$oauthClient->authUrl = $authUrl;
$requestTokenToken = 'test_request_token';
$requestToken = new OAuthToken();
$requestToken->setToken($requestTokenToken);
$builtAuthUrl = $oauthClient->buildAuthUrl($requestToken);
$this->assertContains($authUrl, $builtAuthUrl, 'No auth URL present!');
$this->assertContains($requestTokenToken, $builtAuthUrl, 'No token present!');
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\oauth;
use yii\authclient\OAuth2;
use yiiunit\extensions\authclient\TestCase;
class OAuth2Test extends TestCase
{
protected function setUp()
{
$this->mockApplication([], '\yii\web\Application');
}
// Tests :
public function testBuildAuthUrl()
{
$oauthClient = new OAuth2();
$authUrl = 'http://test.auth.url';
$oauthClient->authUrl = $authUrl;
$clientId = 'test_client_id';
$oauthClient->clientId = $clientId;
$returnUrl = 'http://test.return.url';
$oauthClient->setReturnUrl($returnUrl);
$builtAuthUrl = $oauthClient->buildAuthUrl();
$this->assertContains($authUrl, $builtAuthUrl, 'No auth URL present!');
$this->assertContains($clientId, $builtAuthUrl, 'No client id present!');
$this->assertContains(rawurlencode($returnUrl), $builtAuthUrl, 'No return URL present!');
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient;
use yii\authclient\OpenId;
class OpenIdTest extends TestCase
{
protected function setUp()
{
$config = [
'components' => [
'request' => [
'hostInfo' => 'http://testdomain.com',
'scriptUrl' => '/index.php',
],
]
];
$this->mockApplication($config, '\yii\web\Application');
}
// Tests :
public function testSetGet()
{
$client = new OpenId();
$trustRoot = 'http://trust.root';
$client->setTrustRoot($trustRoot);
$this->assertEquals($trustRoot, $client->getTrustRoot(), 'Unable to setup trust root!');
$returnUrl = 'http://return.url';
$client->setReturnUrl($returnUrl);
$this->assertEquals($returnUrl, $client->getReturnUrl(), 'Unable to setup return URL!');
}
/**
* @depends testSetGet
*/
public function testGetDefaults()
{
$client = new OpenId();
$this->assertNotEmpty($client->getTrustRoot(), 'Unable to get default trust root!');
$this->assertNotEmpty($client->getReturnUrl(), 'Unable to get default return URL!');
}
public function testDiscover()
{
$url = 'https://www.google.com/accounts/o8/id';
$client = new OpenId();
$info = $client->discover($url);
$this->assertNotEmpty($info);
$this->assertNotEmpty($info['url']);
$this->assertNotEmpty($info['identity']);
$this->assertEquals(2, $info['version']);
$this->assertArrayHasKey('identifier_select', $info);
$this->assertArrayHasKey('ax', $info);
$this->assertArrayHasKey('sreg', $info);
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient;
use yii\helpers\FileHelper;
use Yii;
/**
* TestCase for "authclient" extension.
*/
class TestCase extends \yiiunit\TestCase
{
/**
* Adds sphinx extension files to [[Yii::$classPath]],
* avoiding the necessity of usage Composer autoloader.
*/
public static function loadClassMap()
{
$baseNameSpace = 'yii/authclient';
$basePath = realpath(__DIR__. '/../../../../extensions/yii/authclient');
$files = FileHelper::findFiles($basePath);
foreach ($files as $file) {
$classRelativePath = str_replace($basePath, '', $file);
$classFullName = str_replace(['/', '.php'], ['\\', ''], $baseNameSpace . $classRelativePath);
Yii::$classMap[$classFullName] = $file;
}
}
}
TestCase::loadClassMap();
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\oauth;
use yii\authclient\OAuthToken;
use yiiunit\extensions\authclient\TestCase;
class TokenTest extends TestCase
{
public function testCreate()
{
$config = [
'tokenParamKey' => 'test_token_param_key',
'tokenSecretParamKey' => 'test_token_secret_param_key',
];
$oauthToken = new OAuthToken($config);
$this->assertTrue(is_object($oauthToken), 'Unable to create access token!');
foreach ($config as $name => $value) {
$this->assertEquals($value, $oauthToken->$name, 'Unable to setup attributes by constructor!');
}
$this->assertTrue($oauthToken->createTimestamp > 0, 'Unable to fill create timestamp!');
}
public function testSetupParams()
{
$oauthToken = new OAuthToken();
$params = [
'name_1' => 'value_1',
'name_2' => 'value_2',
];
$oauthToken->setParams($params);
$this->assertEquals($params, $oauthToken->getParams(), 'Unable to setup params!');
$newParamName = 'new_param_name';
$newParamValue = 'new_param_value';
$oauthToken->setParam($newParamName, $newParamValue);
$this->assertEquals($newParamValue, $oauthToken->getParam($newParamName), 'Unable to setup param by name!');
}
/**
* @depends testSetupParams
*/
public function testSetupParamsShortcuts()
{
$oauthToken = new OAuthToken();
$token = 'test_token_value';
$oauthToken->setToken($token);
$this->assertEquals($token, $oauthToken->getToken(), 'Unable to setup token!');
$tokenSecret = 'test_token_secret';
$oauthToken->setTokenSecret($tokenSecret);
$this->assertEquals($tokenSecret, $oauthToken->getTokenSecret(), 'Unable to setup token secret!');
$tokenExpireDuration = rand(1000, 2000);
$oauthToken->setExpireDuration($tokenExpireDuration);
$this->assertEquals($tokenExpireDuration, $oauthToken->getExpireDuration(), 'Unable to setup expire duration!');
}
/**
* Data provider for {@link testAutoFetchExpireDuration}.
* @return array test data.
*/
public function autoFetchExpireDurationDataProvider()
{
return [
[
['expire_in' => 123345],
123345
],
[
['expire' => 233456],
233456
],
[
['expiry_in' => 34567],
34567
],
[
['expiry' => 45678],
45678
],
];
}
/**
* @depends testSetupParamsShortcuts
* @dataProvider autoFetchExpireDurationDataProvider
*
* @param array $params
* @param $expectedExpireDuration
*/
public function testAutoFetchExpireDuration(array $params, $expectedExpireDuration)
{
$oauthToken = new OAuthToken();
$oauthToken->setParams($params);
$this->assertEquals($expectedExpireDuration, $oauthToken->getExpireDuration());
}
/**
* @depends testSetupParamsShortcuts
*/
public function testGetIsExpired()
{
$oauthToken = new OAuthToken();
$expireDuration = 3600;
$oauthToken->setExpireDuration($expireDuration);
$this->assertFalse($oauthToken->getIsExpired(), 'Not expired token check fails!');
$oauthToken->createTimestamp = $oauthToken->createTimestamp - ($expireDuration +1);
$this->assertTrue($oauthToken->getIsExpired(), 'Expired token check fails!');
}
/**
* @depends testGetIsExpired
*/
public function testGetIsValid()
{
$oauthToken = new OAuthToken();
$expireDuration = 3600;
$oauthToken->setExpireDuration($expireDuration);
$this->assertFalse($oauthToken->getIsValid(), 'Empty token is valid!');
$oauthToken->setToken('test_token');
$this->assertTrue($oauthToken->getIsValid(), 'Filled up token is invalid!');
$oauthToken->createTimestamp = $oauthToken->createTimestamp - ($expireDuration +1);
$this->assertFalse($oauthToken->getIsValid(), 'Expired token is valid!');
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\signature;
use yiiunit\extensions\authclient\TestCase;
class BaseMethodTest extends TestCase
{
/**
* Creates test signature method instance.
* @return \yii\authclient\signature\BaseMethod
*/
protected function createTestSignatureMethod()
{
$signatureMethod = $this->getMock('\yii\authclient\signature\BaseMethod', ['getName', 'generateSignature']);
$signatureMethod->expects($this->any())->method('getName')->will($this->returnValue('testMethodName'));
$signatureMethod->expects($this->any())->method('generateSignature')->will($this->returnValue('testSignature'));
return $signatureMethod;
}
// Tests :
public function testGenerateSignature()
{
$signatureMethod = $this->createTestSignatureMethod();
$baseString = 'test_base_string';
$key = 'test_key';
$signature = $signatureMethod->generateSignature($baseString, $key);
$this->assertNotEmpty($signature, 'Unable to generate signature!');
}
/**
* @depends testGenerateSignature
*/
public function testVerify()
{
$signatureMethod = $this->createTestSignatureMethod();
$baseString = 'test_base_string';
$key = 'test_key';
$signature = 'unsigned';
$this->assertFalse($signatureMethod->verify($signature, $baseString, $key), 'Unsigned signature is valid!');
$generatedSignature = $signatureMethod->generateSignature($baseString, $key);
$this->assertTrue($signatureMethod->verify($generatedSignature, $baseString, $key), 'Generated signature is invalid!');
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\signature;
use yii\authclient\signature\HmacSha1;
use yiiunit\extensions\authclient\TestCase;
class HmacSha1Test extends TestCase
{
public function testGenerateSignature()
{
$signatureMethod = new HmacSha1();
$baseString = 'test_base_string';
$key = 'test_key';
$signature = $signatureMethod->generateSignature($baseString, $key);
$this->assertNotEmpty($signature, 'Unable to generate signature!');
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\oauth\signature;
use yii\authclient\signature\PlainText;
use yiiunit\extensions\authclient\TestCase;
class PlainTextTest extends TestCase
{
public function testGenerateSignature()
{
$signatureMethod = new PlainText();
$baseString = 'test_base_string';
$key = 'test_key';
$signature = $signatureMethod->generateSignature($baseString, $key);
$this->assertNotEmpty($signature, 'Unable to generate signature!');
}
}
\ No newline at end of file
<?php
namespace yiiunit\extensions\authclient\oauth\signature;
use yii\authclient\signature\RsaSha1;
use yiiunit\extensions\authclient\TestCase;
class RsaSha1Test extends TestCase
{
/**
* Returns test public certificate string.
* @return string public certificate string.
*/
protected function getTestPublicCertificate()
{
return '-----BEGIN CERTIFICATE-----
MIIDJDCCAo2gAwIBAgIJALCFAl3nj1ibMA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD
VQQGEwJOTDESMBAGA1UECAwJQW1zdGVyZGFtMRIwEAYDVQQHDAlBbXN0ZXJkYW0x
DzANBgNVBAoMBlBpbVRpbTEPMA0GA1UECwwGUGltVGltMSswKQYDVQQDDCJkZXY1
My5xdWFydHNvZnQuY29tL3BrbGltb3YvcGltdGltMSQwIgYJKoZIhvcNAQkBFhVw
a2xpbW92QHF1YXJ0c29mdC5jb20wHhcNMTIxMTA2MTQxNjUzWhcNMTMxMTA2MTQx
NjUzWjCBqjELMAkGA1UEBhMCTkwxEjAQBgNVBAgMCUFtc3RlcmRhbTESMBAGA1UE
BwwJQW1zdGVyZGFtMQ8wDQYDVQQKDAZQaW1UaW0xDzANBgNVBAsMBlBpbVRpbTEr
MCkGA1UEAwwiZGV2NTMucXVhcnRzb2Z0LmNvbS9wa2xpbW92L3BpbXRpbTEkMCIG
CSqGSIb3DQEJARYVcGtsaW1vdkBxdWFydHNvZnQuY29tMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDE0d63YwpBLxzxQAW887JALcGruAHkHu7Ui1oc7bCIMy+u
d6rPgNmbFLw3GoGzQ8xhMmksZHsS07IfWRTDeisPHAqfgcApOZbyMyZUAL6+1ko4
xAIPnQSia7l8M4nWgtgqifDCbFKAoPXuWSrYDOFtgSkBLH5xYyFPRc04nnHpoQID
AQABo1AwTjAdBgNVHQ4EFgQUE2oxXYDFRNtgvn8tyXldepRFWzYwHwYDVR0jBBgw
FoAUE2oxXYDFRNtgvn8tyXldepRFWzYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
AQUFAAOBgQB1/S46dWBECaOs4byCysFhzXw8qx8znJkSZcIdDilmg1kkfusXKi2S
DiiFw5gDrc6Qp6WtPmVhxHUWl6O5bOG8lG0Dcppeed9454CGvBShmYdwC6vk0s7/
gVdK2V4fYsUeT6u49ONshvJ/8xhHz2gGXeLWaqHwtK3Dl3S6TIDuoQ==
-----END CERTIFICATE-----';
}
/**
* Returns test private certificate string.
* @return string private certificate string.
*/
protected function getTestPrivateCertificate()
{
return '-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDE0d63YwpBLxzxQAW887JALcGruAHkHu7Ui1oc7bCIMy+ud6rP
gNmbFLw3GoGzQ8xhMmksZHsS07IfWRTDeisPHAqfgcApOZbyMyZUAL6+1ko4xAIP
nQSia7l8M4nWgtgqifDCbFKAoPXuWSrYDOFtgSkBLH5xYyFPRc04nnHpoQIDAQAB
AoGAPm1e2gYE86Xw5ShsaYFWcXrR6hiEKQoSsMG+hFxz2M97eTglqolw+/p4tHWo
2+ZORioKJ/V6//67iavkpRfz3dloUlNE9ZzlvqvPjHePt3BI22GI8D84dcqnxWW5
4okEAfDfXk2B4UNOpVNU5FZjg4XvBEbbhRVrsBWAPMduDX0CQQDtFgLLLWr50F3z
lGuFy68Y1d01sZsyf7xUPaLcDWbrnVMIjZIs60BbLg9PZ6sYcwV2RwL/WaJU0Ap/
KKjHW51zAkEA1IWBicQtt6yGaUqydq+ifX8/odFjIrlZklwckLl65cImyxqDYMnA
m+QdbZznSH96BHjduhJAEAtfYx5CVMrXmwJAHKiWedzpm3z2fmUoginW5pejf8QS
UI5kQ4KX1yW/lSeVS+lhDBD73Im6zAxqADCXLm7zC87X8oybWDef/0kxxQJAebRX
AalKMSRo+QVg/F0Kpenoa+f4aNtSc2GyriK6QbeU9b0iPZxsZBoXzD0NqlPucX8y
IyvuagHJR379p4dePwJBAMCkYSATGdhYbeDfySWUro5K0QAvBNj8FuNJQ4rqUxz8
8b+OXIyd5WlmuDRTDGJBTxAYeaioTuMCFWaZm4jG0I4=
-----END RSA PRIVATE KEY-----';
}
// Tests :
public function testGenerateSignature()
{
$signatureMethod = new RsaSha1();
$signatureMethod->setPrivateCertificate($this->getTestPrivateCertificate());
$signatureMethod->setPublicCertificate($this->getTestPublicCertificate());
$baseString = 'test_base_string';
$key = 'test_key';
$signature = $signatureMethod->generateSignature($baseString, $key);
$this->assertNotEmpty($signature, 'Unable to generate signature!');
}
/**
* @depends testGenerateSignature
*/
public function testVerify()
{
$signatureMethod = new RsaSha1();
$signatureMethod->setPrivateCertificate($this->getTestPrivateCertificate());
$signatureMethod->setPublicCertificate($this->getTestPublicCertificate());
$baseString = 'test_base_string';
$key = 'test_key';
$signature = 'unsigned';
$this->assertFalse($signatureMethod->verify($signature, $baseString, $key), 'Unsigned signature is valid!');
$generatedSignature = $signatureMethod->generateSignature($baseString, $key);
$this->assertTrue($signatureMethod->verify($generatedSignature, $baseString, $key), 'Generated signature is invalid!');
}
public function testInitPrivateCertificate()
{
$signatureMethod = new RsaSha1();
$certificateFileName = __FILE__;
$signatureMethod->privateCertificateFile = $certificateFileName;
$this->assertEquals(file_get_contents($certificateFileName), $signatureMethod->getPrivateCertificate(), 'Unable to fetch private certificate from file!');
}
public function testInitPublicCertificate()
{
$signatureMethod = new RsaSha1();
$certificateFileName = __FILE__;
$signatureMethod->publicCertificateFile = $certificateFileName;
$this->assertEquals(file_get_contents($certificateFileName), $signatureMethod->getPublicCertificate(), 'Unable to fetch public certificate from file!');
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment