Qiang Xue
11 years ago
43 changed files with 5629 additions and 0 deletions
@ -0,0 +1,359 @@
|
||||
<?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); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,237 @@
|
||||
<?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; |
||||
} |
||||
} |
@ -0,0 +1,503 @@
|
||||
<?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\Exception; |
||||
use yii\base\InvalidParamException; |
||||
use Yii; |
||||
use yii\helpers\Json; |
||||
|
||||
/** |
||||
* BaseClient is a base class for the OAuth clients. |
||||
* |
||||
* @see http://oauth.net/ |
||||
* |
||||
* @author Paul Klimov <klimov.paul@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
abstract class BaseOAuth extends BaseClient implements ClientInterface |
||||
{ |
||||
const CONTENT_TYPE_JSON = 'json'; // JSON format |
||||
const CONTENT_TYPE_URLENCODED = 'urlencoded'; // urlencoded query string, like name1=value1&name2=value2 |
||||
const CONTENT_TYPE_XML = 'xml'; // XML format |
||||
const CONTENT_TYPE_AUTO = 'auto'; // attempts to determine format automatically |
||||
|
||||
/** |
||||
* @var string protocol version. |
||||
*/ |
||||
public $version = '1.0'; |
||||
/** |
||||
* @var string URL, which user will be redirected after authentication at the OAuth provider web site. |
||||
* Note: this should be absolute URL (with http:// or https:// leading). |
||||
* By default current URL will be used. |
||||
*/ |
||||
private $_returnUrl; |
||||
/** |
||||
* @var string API base URL. |
||||
*/ |
||||
public $apiBaseUrl; |
||||
/** |
||||
* @var string authorize URL. |
||||
*/ |
||||
public $authUrl; |
||||
/** |
||||
* @var string auth request scope. |
||||
*/ |
||||
public $scope; |
||||
/** |
||||
* @var array cURL request options. Option values from this field will overwrite corresponding |
||||
* values from {@link defaultCurlOptions()}. |
||||
*/ |
||||
private $_curlOptions = []; |
||||
/** |
||||
* @var OAuthToken|array access token instance or its array configuration. |
||||
*/ |
||||
private $_accessToken; |
||||
/** |
||||
* @var signature\BaseMethod|array signature method instance or its array configuration. |
||||
*/ |
||||
private $_signatureMethod = []; |
||||
|
||||
/** |
||||
* @param string $returnUrl return URL |
||||
*/ |
||||
public function setReturnUrl($returnUrl) |
||||
{ |
||||
$this->_returnUrl = $returnUrl; |
||||
} |
||||
|
||||
/** |
||||
* @return string return URL. |
||||
*/ |
||||
public function getReturnUrl() |
||||
{ |
||||
if ($this->_returnUrl === null) { |
||||
$this->_returnUrl = $this->defaultReturnUrl(); |
||||
} |
||||
return $this->_returnUrl; |
||||
} |
||||
|
||||
/** |
||||
* @param array $curlOptions cURL options. |
||||
*/ |
||||
public function setCurlOptions(array $curlOptions) |
||||
{ |
||||
$this->_curlOptions = $curlOptions; |
||||
} |
||||
|
||||
/** |
||||
* @return array cURL options. |
||||
*/ |
||||
public function getCurlOptions() |
||||
{ |
||||
return $this->_curlOptions; |
||||
} |
||||
|
||||
/** |
||||
* @param array|OAuthToken $token |
||||
*/ |
||||
public function setAccessToken($token) |
||||
{ |
||||
if (!is_object($token)) { |
||||
$token = $this->createToken($token); |
||||
} |
||||
$this->_accessToken = $token; |
||||
$this->saveAccessToken($token); |
||||
} |
||||
|
||||
/** |
||||
* @return OAuthToken auth token instance. |
||||
*/ |
||||
public function getAccessToken() |
||||
{ |
||||
if (!is_object($this->_accessToken)) { |
||||
$this->_accessToken = $this->restoreAccessToken(); |
||||
} |
||||
return $this->_accessToken; |
||||
} |
||||
|
||||
/** |
||||
* @param array|signature\BaseMethod $signatureMethod signature method instance or its array configuration. |
||||
* @throws InvalidParamException on wrong argument. |
||||
*/ |
||||
public function setSignatureMethod($signatureMethod) |
||||
{ |
||||
if (!is_object($signatureMethod) && !is_array($signatureMethod)) { |
||||
throw new InvalidParamException('"' . get_class($this) . '::signatureMethod" should be instance of "\yii\autclient\signature\BaseMethod" or its array configuration. "' . gettype($signatureMethod) . '" has been given.'); |
||||
} |
||||
$this->_signatureMethod = $signatureMethod; |
||||
} |
||||
|
||||
/** |
||||
* @return signature\BaseMethod signature method instance. |
||||
*/ |
||||
public function getSignatureMethod() |
||||
{ |
||||
if (!is_object($this->_signatureMethod)) { |
||||
$this->_signatureMethod = $this->createSignatureMethod($this->_signatureMethod); |
||||
} |
||||
return $this->_signatureMethod; |
||||
} |
||||
|
||||
/** |
||||
* Composes default {@link returnUrl} value. |
||||
* @return string return URL. |
||||
*/ |
||||
protected function defaultReturnUrl() |
||||
{ |
||||
return Yii::$app->getRequest()->getAbsoluteUrl(); |
||||
} |
||||
|
||||
/** |
||||
* Sends HTTP request. |
||||
* @param string $method request type. |
||||
* @param string $url request URL. |
||||
* @param array $params request params. |
||||
* @return array response. |
||||
* @throws Exception on failure. |
||||
*/ |
||||
protected function sendRequest($method, $url, array $params = []) |
||||
{ |
||||
$curlOptions = $this->mergeCurlOptions( |
||||
$this->defaultCurlOptions(), |
||||
$this->getCurlOptions(), |
||||
array( |
||||
CURLOPT_RETURNTRANSFER => true, |
||||
CURLOPT_URL => $url, |
||||
), |
||||
$this->composeRequestCurlOptions(strtoupper($method), $url, $params) |
||||
); |
||||
$curlResource = curl_init(); |
||||
foreach ($curlOptions as $option => $value) { |
||||
curl_setopt($curlResource, $option, $value); |
||||
} |
||||
$response = curl_exec($curlResource); |
||||
$responseHeaders = curl_getinfo($curlResource); |
||||
|
||||
// check cURL error |
||||
$errorNumber = curl_errno($curlResource); |
||||
$errorMessage = curl_error($curlResource); |
||||
|
||||
curl_close($curlResource); |
||||
|
||||
if ($errorNumber > 0) { |
||||
throw new Exception('Curl error requesting "' . $url . '": #' . $errorNumber . ' - ' . $errorMessage); |
||||
} |
||||
if ($responseHeaders['http_code'] != 200) { |
||||
throw new Exception('Request failed with code: ' . $responseHeaders['http_code'] . ', message: ' . $response); |
||||
} |
||||
return $this->processResponse($response, $this->determineContentTypeByHeaders($responseHeaders)); |
||||
} |
||||
|
||||
/** |
||||
* Merge CUrl options. |
||||
* If each options array has an element with the same key value, the latter |
||||
* will overwrite the former. |
||||
* @param array $options1 options to be merged to. |
||||
* @param array $options2 options to be merged from. You can specify additional |
||||
* arrays via third argument, fourth argument etc. |
||||
* @return array merged options (the original options are not changed.) |
||||
*/ |
||||
protected function mergeCurlOptions($options1, $options2) |
||||
{ |
||||
$args = func_get_args(); |
||||
$res = array_shift($args); |
||||
while (!empty($args)) { |
||||
$next = array_shift($args); |
||||
foreach ($next as $k => $v) { |
||||
$res[$k]=$v; |
||||
} |
||||
} |
||||
return $res; |
||||
} |
||||
|
||||
/** |
||||
* Returns default cURL options. |
||||
* @return array cURL options. |
||||
*/ |
||||
protected function defaultCurlOptions() |
||||
{ |
||||
return [ |
||||
CURLOPT_USERAGENT => Yii::$app->name . ' OAuth ' . $this->version . ' Client', |
||||
CURLOPT_CONNECTTIMEOUT => 30, |
||||
CURLOPT_TIMEOUT => 30, |
||||
CURLOPT_SSL_VERIFYPEER => false, |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* Processes raw response converting it to actual data. |
||||
* @param string $rawResponse raw response. |
||||
* @param string $contentType response content type. |
||||
* @throws Exception on failure. |
||||
* @return array actual response. |
||||
*/ |
||||
protected function processResponse($rawResponse, $contentType = self::CONTENT_TYPE_AUTO) |
||||
{ |
||||
if (empty($rawResponse)) { |
||||
return []; |
||||
} |
||||
switch ($contentType) { |
||||
case self::CONTENT_TYPE_AUTO: { |
||||
$contentType = $this->determineContentTypeByRaw($rawResponse); |
||||
if ($contentType == self::CONTENT_TYPE_AUTO) { |
||||
throw new Exception('Unable to determine response content type automatically.'); |
||||
} |
||||
$response = $this->processResponse($rawResponse, $contentType); |
||||
break; |
||||
} |
||||
case self::CONTENT_TYPE_JSON: { |
||||
$response = Json::decode($rawResponse, true); |
||||
if (isset($response['error'])) { |
||||
throw new Exception('Response error: ' . $response['error']); |
||||
} |
||||
break; |
||||
} |
||||
case self::CONTENT_TYPE_URLENCODED: { |
||||
$response = []; |
||||
parse_str($rawResponse, $response); |
||||
break; |
||||
} |
||||
case self::CONTENT_TYPE_XML: { |
||||
$response = $this->convertXmlToArray($rawResponse); |
||||
break; |
||||
} |
||||
default: { |
||||
throw new Exception('Unknown response type "' . $contentType . '".'); |
||||
} |
||||
} |
||||
return $response; |
||||
} |
||||
|
||||
/** |
||||
* Converts XML document to array. |
||||
* @param string|\SimpleXMLElement $xml xml to process. |
||||
* @return array XML array representation. |
||||
*/ |
||||
protected function convertXmlToArray($xml) |
||||
{ |
||||
if (!is_object($xml)) { |
||||
$xml = simplexml_load_string($xml); |
||||
} |
||||
$result = (array)$xml; |
||||
foreach ($result as $key => $value) { |
||||
if (is_object($value)) { |
||||
$result[$key] = $this->convertXmlToArray($value); |
||||
} |
||||
} |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Attempts to determine HTTP request content type by headers. |
||||
* @param array $headers request headers. |
||||
* @return string content type. |
||||
*/ |
||||
protected function determineContentTypeByHeaders(array $headers) |
||||
{ |
||||
if (isset($headers['content_type'])) { |
||||
if (stripos($headers['content_type'], 'json') !== false) { |
||||
return self::CONTENT_TYPE_JSON; |
||||
} |
||||
if (stripos($headers['content_type'], 'urlencoded') !== false) { |
||||
return self::CONTENT_TYPE_URLENCODED; |
||||
} |
||||
if (stripos($headers['content_type'], 'xml') !== false) { |
||||
return self::CONTENT_TYPE_XML; |
||||
} |
||||
} |
||||
return self::CONTENT_TYPE_AUTO; |
||||
} |
||||
|
||||
/** |
||||
* Attempts to determine the content type from raw content. |
||||
* @param string $rawContent raw response content. |
||||
* @return string response type. |
||||
*/ |
||||
protected function determineContentTypeByRaw($rawContent) |
||||
{ |
||||
if (preg_match('/^\\{.*\\}$/is', $rawContent)) { |
||||
return self::CONTENT_TYPE_JSON; |
||||
} |
||||
if (preg_match('/^[^=|^&]+=[^=|^&]+(&[^=|^&]+=[^=|^&]+)*$/is', $rawContent)) { |
||||
return self::CONTENT_TYPE_URLENCODED; |
||||
} |
||||
if (preg_match('/^<.*>$/is', $rawContent)) { |
||||
return self::CONTENT_TYPE_XML; |
||||
} |
||||
return self::CONTENT_TYPE_AUTO; |
||||
} |
||||
|
||||
/** |
||||
* Creates signature method instance from its configuration. |
||||
* @param array $signatureMethodConfig signature method configuration. |
||||
* @return signature\BaseMethod signature method instance. |
||||
*/ |
||||
protected function createSignatureMethod(array $signatureMethodConfig) |
||||
{ |
||||
if (!array_key_exists('class', $signatureMethodConfig)) { |
||||
$signatureMethodConfig['class'] = signature\HmacSha1::className(); |
||||
} |
||||
return Yii::createObject($signatureMethodConfig); |
||||
} |
||||
|
||||
/** |
||||
* Creates token from its configuration. |
||||
* @param array $tokenConfig token configuration. |
||||
* @return OAuthToken token instance. |
||||
*/ |
||||
protected function createToken(array $tokenConfig = []) |
||||
{ |
||||
if (!array_key_exists('class', $tokenConfig)) { |
||||
$tokenConfig['class'] = OAuthToken::className(); |
||||
} |
||||
return Yii::createObject($tokenConfig); |
||||
} |
||||
|
||||
/** |
||||
* Composes URL from base URL and GET params. |
||||
* @param string $url base URL. |
||||
* @param array $params GET params. |
||||
* @return string composed URL. |
||||
*/ |
||||
protected function composeUrl($url, array $params = []) |
||||
{ |
||||
if (strpos($url, '?') === false) { |
||||
$url .= '?'; |
||||
} else { |
||||
$url .= '&'; |
||||
} |
||||
$url .= http_build_query($params, '', '&', PHP_QUERY_RFC3986); |
||||
return $url; |
||||
} |
||||
|
||||
/** |
||||
* Saves token as persistent state. |
||||
* @param OAuthToken $token auth token |
||||
* @return static self reference. |
||||
*/ |
||||
protected function saveAccessToken(OAuthToken $token) |
||||
{ |
||||
return $this->setState('token', $token); |
||||
} |
||||
|
||||
/** |
||||
* Restores access token. |
||||
* @return OAuthToken auth token. |
||||
*/ |
||||
protected function restoreAccessToken() |
||||
{ |
||||
$token = $this->getState('token'); |
||||
if (is_object($token)) { |
||||
/* @var $token OAuthToken */ |
||||
if ($token->getIsExpired()) { |
||||
$token = $this->refreshAccessToken($token); |
||||
} |
||||
} |
||||
return $token; |
||||
} |
||||
|
||||
/** |
||||
* Sets persistent state. |
||||
* @param string $key state key. |
||||
* @param mixed $value state value |
||||
* @return static self reference. |
||||
*/ |
||||
protected function setState($key, $value) |
||||
{ |
||||
$session = Yii::$app->getSession(); |
||||
$key = $this->getStateKeyPrefix() . $key; |
||||
$session->set($key, $value); |
||||
return $this; |
||||
} |
||||
|
||||
/** |
||||
* Returns persistent state value. |
||||
* @param string $key state key. |
||||
* @return mixed state value. |
||||
*/ |
||||
protected function getState($key) |
||||
{ |
||||
$session = Yii::$app->getSession(); |
||||
$key = $this->getStateKeyPrefix() . $key; |
||||
$value = $session->get($key); |
||||
return $value; |
||||
} |
||||
|
||||
/** |
||||
* Removes persistent state value. |
||||
* @param string $key state key. |
||||
* @return boolean success. |
||||
*/ |
||||
protected function removeState($key) |
||||
{ |
||||
$session = Yii::$app->getSession(); |
||||
$key = $this->getStateKeyPrefix() . $key; |
||||
$session->remove($key); |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Returns session key prefix, which is used to store internal states. |
||||
* @return string session key prefix. |
||||
*/ |
||||
protected function getStateKeyPrefix() |
||||
{ |
||||
return get_class($this) . '_' . sha1($this->authUrl) . '_'; |
||||
} |
||||
|
||||
/** |
||||
* Performs request to the OAuth API. |
||||
* @param string $apiSubUrl API sub URL, which will be append to [[apiBaseUrl]], or absolute API URL. |
||||
* @param string $method request method. |
||||
* @param array $params request parameters. |
||||
* @return array API response |
||||
* @throws Exception on failure. |
||||
*/ |
||||
public function api($apiSubUrl, $method = 'GET', array $params = []) |
||||
{ |
||||
if (preg_match('/^https?:\\/\\//is', $apiSubUrl)) { |
||||
$url = $apiSubUrl; |
||||
} else { |
||||
$url = $this->apiBaseUrl . '/' . $apiSubUrl; |
||||
} |
||||
$accessToken = $this->getAccessToken(); |
||||
if (!is_object($accessToken) || !$accessToken->getIsValid()) { |
||||
throw new Exception('Invalid access token.'); |
||||
} |
||||
return $this->apiInternal($accessToken, $url, $method, $params); |
||||
} |
||||
|
||||
/** |
||||
* 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. |
||||
*/ |
||||
abstract protected function composeRequestCurlOptions($method, $url, array $params); |
||||
|
||||
/** |
||||
* Gets new auth token to replace expired one. |
||||
* @param OAuthToken $token expired auth token. |
||||
* @return OAuthToken new auth token. |
||||
*/ |
||||
abstract public function refreshAccessToken(OAuthToken $token); |
||||
|
||||
/** |
||||
* 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. |
||||
*/ |
||||
abstract protected function apiInternal($accessToken, $url, $method, array $params); |
||||
} |
@ -0,0 +1,57 @@
|
||||
<?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(); |
||||
} |
@ -0,0 +1,107 @@
|
||||
<?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); |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
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. |
@ -0,0 +1,354 @@
|
||||
<?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\Exception; |
||||
use Yii; |
||||
|
||||
/** |
||||
* OAuth1 serves as a client for the OAuth 1/1.0a flow. |
||||
* |
||||
* In oder to acquire access token perform following sequence: |
||||
* |
||||
* ~~~ |
||||
* use yii\authclient\OAuth1; |
||||
* |
||||
* $oauthClient = new OAuth1(); |
||||
* $requestToken = $oauthClient->fetchRequestToken(); // Get request token |
||||
* $url = $oauthClient->buildAuthUrl($requestToken); // Get authorization URL |
||||
* return Yii::$app->getResponse()->redirect($url); // Redirect to authorization URL |
||||
* // After user returns at our site: |
||||
* $accessToken = $oauthClient->fetchAccessToken($requestToken); // Upgrade to access token |
||||
* ~~~ |
||||
* |
||||
* @see http://oauth.net/ |
||||
* |
||||
* @author Paul Klimov <klimov.paul@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class OAuth1 extends BaseOAuth |
||||
{ |
||||
/** |
||||
* @var string protocol version. |
||||
*/ |
||||
public $version = '1.0'; |
||||
/** |
||||
* @var string OAuth consumer key. |
||||
*/ |
||||
public $consumerKey; |
||||
/** |
||||
* @var string OAuth consumer secret. |
||||
*/ |
||||
public $consumerSecret; |
||||
/** |
||||
* @var string OAuth request token URL. |
||||
*/ |
||||
public $requestTokenUrl; |
||||
/** |
||||
* @var string request token HTTP method. |
||||
*/ |
||||
public $requestTokenMethod = 'GET'; |
||||
/** |
||||
* @var string OAuth access token URL. |
||||
*/ |
||||
public $accessTokenUrl; |
||||
/** |
||||
* @var string access token HTTP method. |
||||
*/ |
||||
public $accessTokenMethod = 'GET'; |
||||
|
||||
/** |
||||
* Fetches the OAuth request token. |
||||
* @param array $params additional request params. |
||||
* @return OAuthToken request token. |
||||
*/ |
||||
public function fetchRequestToken(array $params = []) |
||||
{ |
||||
$this->removeState('token'); |
||||
$defaultParams = [ |
||||
'oauth_consumer_key' => $this->consumerKey, |
||||
'oauth_callback' => $this->getReturnUrl(), |
||||
//'xoauth_displayname' => Yii::$app->name, |
||||
]; |
||||
if (!empty($this->scope)) { |
||||
$defaultParams['scope'] = $this->scope; |
||||
} |
||||
$response = $this->sendSignedRequest($this->requestTokenMethod, $this->requestTokenUrl, array_merge($defaultParams, $params)); |
||||
$token = $this->createToken([ |
||||
'params' => $response |
||||
]); |
||||
$this->setState('requestToken', $token); |
||||
return $token; |
||||
} |
||||
|
||||
/** |
||||
* Composes user authorization URL. |
||||
* @param OAuthToken $requestToken OAuth request token. |
||||
* @param array $params additional request params. |
||||
* @return string authorize URL |
||||
* @throws Exception on failure. |
||||
*/ |
||||
public function buildAuthUrl(OAuthToken $requestToken = null, array $params = []) |
||||
{ |
||||
if (!is_object($requestToken)) { |
||||
$requestToken = $this->getState('requestToken'); |
||||
if (!is_object($requestToken)) { |
||||
throw new Exception('Request token is required to build authorize URL!'); |
||||
} |
||||
} |
||||
$params['oauth_token'] = $requestToken->getToken(); |
||||
return $this->composeUrl($this->authUrl, $params); |
||||
} |
||||
|
||||
/** |
||||
* Fetches OAuth access token. |
||||
* @param OAuthToken $requestToken OAuth request token. |
||||
* @param string $oauthVerifier OAuth verifier. |
||||
* @param array $params additional request params. |
||||
* @return OAuthToken OAuth access token. |
||||
* @throws Exception on failure. |
||||
*/ |
||||
public function fetchAccessToken(OAuthToken $requestToken = null, $oauthVerifier = null, array $params = []) |
||||
{ |
||||
if (!is_object($requestToken)) { |
||||
$requestToken = $this->getState('requestToken'); |
||||
if (!is_object($requestToken)) { |
||||
throw new Exception('Request token is required to fetch access token!'); |
||||
} |
||||
} |
||||
$this->removeState('requestToken'); |
||||
$defaultParams = [ |
||||
'oauth_consumer_key' => $this->consumerKey, |
||||
'oauth_token' => $requestToken->getToken() |
||||
]; |
||||
if ($oauthVerifier === null) { |
||||
if (isset($_REQUEST['oauth_verifier'])) { |
||||
$oauthVerifier = $_REQUEST['oauth_verifier']; |
||||
} |
||||
} |
||||
if (!empty($oauthVerifier)) { |
||||
$defaultParams['oauth_verifier'] = $oauthVerifier; |
||||
} |
||||
$response = $this->sendSignedRequest($this->accessTokenMethod, $this->accessTokenUrl, array_merge($defaultParams, $params)); |
||||
|
||||
$token = $this->createToken([ |
||||
'params' => $response |
||||
]); |
||||
$this->setAccessToken($token); |
||||
return $token; |
||||
} |
||||
|
||||
/** |
||||
* Sends HTTP request, signed by {@link signatureMethod}. |
||||
* @param string $method request type. |
||||
* @param string $url request URL. |
||||
* @param array $params request params. |
||||
* @return array response. |
||||
*/ |
||||
protected function sendSignedRequest($method, $url, array $params = []) |
||||
{ |
||||
$params = array_merge($params, $this->generateCommonRequestParams()); |
||||
$params = $this->signRequest($method, $url, $params); |
||||
return $this->sendRequest($method, $url, $params); |
||||
} |
||||
|
||||
/** |
||||
* 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; |
||||
if (!empty($params)){ |
||||
$curlOptions[CURLOPT_POSTFIELDS] = $params; |
||||
} |
||||
$authorizationHeader = $this->composeAuthorizationHeader($params); |
||||
if (!empty($authorizationHeader)) { |
||||
$curlOptions[CURLOPT_HTTPHEADER] = ['Content-Type: application/atom+xml', $authorizationHeader]; |
||||
} |
||||
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['oauth_consumer_key'] = $this->consumerKey; |
||||
$params['oauth_token'] = $accessToken->getToken(); |
||||
$response = $this->sendSignedRequest($method, $url, $params); |
||||
return $response; |
||||
} |
||||
|
||||
/** |
||||
* Gets new auth token to replace expired one. |
||||
* @param OAuthToken $token expired auth token. |
||||
* @return OAuthToken new auth token. |
||||
*/ |
||||
public function refreshAccessToken(OAuthToken $token) |
||||
{ |
||||
// @todo |
||||
return null; |
||||
} |
||||
|
||||
/** |
||||
* Composes default {@link returnUrl} value. |
||||
* @return string return URL. |
||||
*/ |
||||
protected function defaultReturnUrl() |
||||
{ |
||||
$params = $_GET; |
||||
unset($params['oauth_token']); |
||||
return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params); |
||||
} |
||||
|
||||
/** |
||||
* Generates nonce value. |
||||
* @return string nonce value. |
||||
*/ |
||||
protected function generateNonce() |
||||
{ |
||||
return md5(microtime() . mt_rand()); |
||||
} |
||||
|
||||
/** |
||||
* Generates timestamp. |
||||
* @return integer timestamp. |
||||
*/ |
||||
protected function generateTimestamp() |
||||
{ |
||||
return time(); |
||||
} |
||||
|
||||
/** |
||||
* Generate common request params like version, timestamp etc. |
||||
* @return array common request params. |
||||
*/ |
||||
protected function generateCommonRequestParams() |
||||
{ |
||||
$params = [ |
||||
'oauth_version' => $this->version, |
||||
'oauth_nonce' => $this->generateNonce(), |
||||
'oauth_timestamp' => $this->generateTimestamp(), |
||||
]; |
||||
return $params; |
||||
} |
||||
|
||||
/** |
||||
* Sign request with {@link signatureMethod}. |
||||
* @param string $method request method. |
||||
* @param string $url request URL. |
||||
* @param array $params request params. |
||||
* @return array signed request params. |
||||
*/ |
||||
protected function signRequest($method, $url, array $params) |
||||
{ |
||||
$signatureMethod = $this->getSignatureMethod(); |
||||
$params['oauth_signature_method'] = $signatureMethod->getName(); |
||||
$signatureBaseString = $this->composeSignatureBaseString($method, $url, $params); |
||||
$signatureKey = $this->composeSignatureKey(); |
||||
$params['oauth_signature'] = $signatureMethod->generateSignature($signatureBaseString, $signatureKey); |
||||
return $params; |
||||
} |
||||
|
||||
/** |
||||
* Creates signature base string, which will be signed by {@link signatureMethod}. |
||||
* @param string $method request method. |
||||
* @param string $url request URL. |
||||
* @param array $params request params. |
||||
* @return string base signature string. |
||||
*/ |
||||
protected function composeSignatureBaseString($method, $url, array $params) |
||||
{ |
||||
unset($params['oauth_signature']); |
||||
uksort($params, 'strcmp'); // Parameters are sorted by name, using lexicographical byte value ordering. Ref: Spec: 9.1.1 |
||||
$parts = [ |
||||
strtoupper($method), |
||||
$url, |
||||
http_build_query($params, '', '&', PHP_QUERY_RFC3986) |
||||
]; |
||||
$parts = array_map('rawurlencode', $parts); |
||||
return implode('&', $parts); |
||||
} |
||||
|
||||
/** |
||||
* Composes request signature key. |
||||
* @return string signature key. |
||||
*/ |
||||
protected function composeSignatureKey() |
||||
{ |
||||
$signatureKeyParts = [ |
||||
$this->consumerSecret |
||||
]; |
||||
$accessToken = $this->getAccessToken(); |
||||
if (is_object($accessToken)) { |
||||
$signatureKeyParts[] = $accessToken->getTokenSecret(); |
||||
} else { |
||||
$signatureKeyParts[] = ''; |
||||
} |
||||
$signatureKeyParts = array_map('rawurlencode', $signatureKeyParts); |
||||
return implode('&', $signatureKeyParts); |
||||
} |
||||
|
||||
/** |
||||
* Composes authorization header content. |
||||
* @param array $params request params. |
||||
* @param string $realm authorization realm. |
||||
* @return string authorization header content. |
||||
*/ |
||||
protected function composeAuthorizationHeader(array $params, $realm = '') |
||||
{ |
||||
$header = 'Authorization: OAuth'; |
||||
$headerParams = []; |
||||
if (!empty($realm)) { |
||||
$headerParams[] = 'realm="' . rawurlencode($realm) . '"'; |
||||
} |
||||
foreach ($params as $key => $value) { |
||||
if (substr($key, 0, 5) != 'oauth') { |
||||
continue; |
||||
} |
||||
$headerParams[] = rawurlencode($key) . '="' . rawurlencode($value) . '"'; |
||||
} |
||||
if (!empty($headerParams)) { |
||||
$header .= ' ' . implode(', ', $headerParams); |
||||
} |
||||
return $header; |
||||
} |
||||
} |
@ -0,0 +1,184 @@
|
||||
<?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); |
||||
} |
||||
} |
@ -0,0 +1,186 @@
|
||||
<?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()); |
||||
} |
||||
} |
@ -0,0 +1,928 @@
|
||||
<?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\Exception; |
||||
use yii\base\NotSupportedException; |
||||
use Yii; |
||||
|
||||
/** |
||||
* OpenId provides a simple interface for OpenID (1.1 and 2.0) authentication. |
||||
* Supports Yadis and HTML discovery. |
||||
* |
||||
* Usage: |
||||
* |
||||
* ~~~ |
||||
* use yii\authclient\OpenId; |
||||
* |
||||
* $client = new OpenId(); |
||||
* $client->authUrl = 'https://open.id.provider.url'; // Setup provider endpoint |
||||
* $url = $client->buildAuthUrl(); // Get authentication URL |
||||
* return Yii::$app->getResponse()->redirect($url); // Redirect to authentication URL |
||||
* // After user returns at our site: |
||||
* if ($client->validate()) { // validate response |
||||
* $userAttributes = $client->getUserAttributes(); // get account info |
||||
* ... |
||||
* } |
||||
* ~~~ |
||||
* |
||||
* AX and SREG extensions are supported. |
||||
* To use them, specify [[requiredAttributes]] and/or [[optionalAttributes]]. |
||||
* |
||||
* @see http://openid.net/ |
||||
* |
||||
* @property string $returnUrl authentication return URL. |
||||
* @property string $claimedId claimed identifier (identity). |
||||
* @property string $trustRoot client trust root (realm), by default [[\yii\web\Request::hostInfo]] value will be used. |
||||
* |
||||
* @author Paul Klimov <klimov.paul@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class OpenId extends BaseClient implements ClientInterface |
||||
{ |
||||
/** |
||||
* @var string authentication base URL, which should be used to compose actual authentication URL |
||||
* by [[buildAuthUrl()]] method. |
||||
*/ |
||||
public $authUrl; |
||||
/** |
||||
* @var array list of attributes, which always should be returned from server. |
||||
* Attribute names should be always specified in AX format. |
||||
* For example: |
||||
* ~~~ |
||||
* ['namePerson/friendly', 'contact/email'] |
||||
* ~~~ |
||||
*/ |
||||
public $requiredAttributes = []; |
||||
/** |
||||
* @var array list of attributes, which could be returned from server. |
||||
* Attribute names should be always specified in AX format. |
||||
* For example: |
||||
* ~~~ |
||||
* ['namePerson/first', 'namePerson/last'] |
||||
* ~~~ |
||||
*/ |
||||
public $optionalAttributes = []; |
||||
|
||||
/** |
||||
* @var boolean whether to verify the peer's certificate. |
||||
*/ |
||||
public $verifyPeer; |
||||
/** |
||||
* @var string directory that holds multiple CA certificates. |
||||
* This value will take effect only if [[verifyPeer]] is set. |
||||
*/ |
||||
public $capath; |
||||
/** |
||||
* @var string the name of a file holding one or more certificates to verify the peer with. |
||||
* This value will take effect only if [[verifyPeer]] is set. |
||||
*/ |
||||
public $cainfo; |
||||
|
||||
/** |
||||
* @var string authentication return URL. |
||||
*/ |
||||
private $_returnUrl; |
||||
/** |
||||
* @var string claimed identifier (identity) |
||||
*/ |
||||
private $_claimedId; |
||||
/** |
||||
* @var string client trust root (realm), by default [[\yii\web\Request::hostInfo]] value will be used. |
||||
*/ |
||||
private $_trustRoot; |
||||
/** |
||||
* @var array data, which should be used to retrieve the OpenID response. |
||||
* If not set combination of GET and POST will be used. |
||||
*/ |
||||
public $data; |
||||
/** |
||||
* @var array map of matches between AX and SREG attribute names in format: axAttributeName => sregAttributeName |
||||
*/ |
||||
public $axToSregMap = [ |
||||
'namePerson/friendly' => 'nickname', |
||||
'contact/email' => 'email', |
||||
'namePerson' => 'fullname', |
||||
'birthDate' => 'dob', |
||||
'person/gender' => 'gender', |
||||
'contact/postalCode/home' => 'postcode', |
||||
'contact/country/home' => 'country', |
||||
'pref/language' => 'language', |
||||
'pref/timezone' => 'timezone', |
||||
]; |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function init() |
||||
{ |
||||
if ($this->data === null) { |
||||
$this->data = array_merge($_GET, $_POST); // OPs may send data as POST or GET. |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param string $claimedId claimed identifier (identity). |
||||
*/ |
||||
public function setClaimedId($claimedId) |
||||
{ |
||||
$this->_claimedId = $claimedId; |
||||
} |
||||
|
||||
/** |
||||
* @return string claimed identifier (identity). |
||||
*/ |
||||
public function getClaimedId() |
||||
{ |
||||
if ($this->_claimedId === null) { |
||||
if (isset($this->data['openid_claimed_id'])) { |
||||
$this->_claimedId = $this->data['openid_claimed_id']; |
||||
} elseif (isset($this->data['openid_identity'])) { |
||||
$this->_claimedId = $this->data['openid_identity']; |
||||
} |
||||
} |
||||
return $this->_claimedId; |
||||
} |
||||
|
||||
/** |
||||
* @param string $returnUrl authentication return URL. |
||||
*/ |
||||
public function setReturnUrl($returnUrl) |
||||
{ |
||||
$this->_returnUrl = $returnUrl; |
||||
} |
||||
|
||||
/** |
||||
* @return string authentication return URL. |
||||
*/ |
||||
public function getReturnUrl() |
||||
{ |
||||
if ($this->_returnUrl === null) { |
||||
$this->_returnUrl = $this->defaultReturnUrl(); |
||||
} |
||||
return $this->_returnUrl; |
||||
} |
||||
|
||||
/** |
||||
* @param string $value client trust root (realm). |
||||
*/ |
||||
public function setTrustRoot($value) |
||||
{ |
||||
$this->_trustRoot = $value; |
||||
} |
||||
|
||||
/** |
||||
* @return string client trust root (realm). |
||||
*/ |
||||
public function getTrustRoot() |
||||
{ |
||||
if ($this->_trustRoot === null) { |
||||
$this->_trustRoot = Yii::$app->getRequest()->getHostInfo(); |
||||
} |
||||
return $this->_trustRoot; |
||||
} |
||||
|
||||
/** |
||||
* Generates default [[returnUrl]] value. |
||||
* @return string default authentication return URL. |
||||
*/ |
||||
protected function defaultReturnUrl() |
||||
{ |
||||
$params = $_GET; |
||||
foreach ($params as $name => $value) { |
||||
if (strncmp('openid', $name, 6) === 0) { |
||||
unset($params[$name]); |
||||
} |
||||
} |
||||
$url = Yii::$app->getUrlManager()->createUrl(Yii::$app->requestedRoute, $params); |
||||
return $this->getTrustRoot() . $url; |
||||
} |
||||
|
||||
/** |
||||
* Checks if the server specified in the url exists. |
||||
* @param string $url URL to check |
||||
* @return boolean true, if the server exists; false otherwise |
||||
*/ |
||||
public function hostExists($url) |
||||
{ |
||||
if (strpos($url, '/') === false) { |
||||
$server = $url; |
||||
} else { |
||||
$server = @parse_url($url, PHP_URL_HOST); |
||||
} |
||||
if (!$server) { |
||||
return false; |
||||
} |
||||
$ips = gethostbynamel($server); |
||||
return !empty($ips); |
||||
} |
||||
|
||||
/** |
||||
* Sends HTTP request. |
||||
* @param string $url request URL. |
||||
* @param string $method request method. |
||||
* @param array $params request params. |
||||
* @return array|string response. |
||||
* @throws \yii\base\Exception on failure. |
||||
*/ |
||||
protected function sendCurlRequest($url, $method = 'GET', $params = []) |
||||
{ |
||||
$params = http_build_query($params, '', '&'); |
||||
$curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : '')); |
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); |
||||
curl_setopt($curl, CURLOPT_HEADER, false); |
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); |
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*')); |
||||
|
||||
if ($this->verifyPeer !== null) { |
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifyPeer); |
||||
if($this->capath) { |
||||
curl_setopt($curl, CURLOPT_CAPATH, $this->capath); |
||||
} |
||||
if($this->cainfo) { |
||||
curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo); |
||||
} |
||||
} |
||||
|
||||
if ($method == 'POST') { |
||||
curl_setopt($curl, CURLOPT_POST, true); |
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $params); |
||||
} elseif ($method == 'HEAD') { |
||||
curl_setopt($curl, CURLOPT_HEADER, true); |
||||
curl_setopt($curl, CURLOPT_NOBODY, true); |
||||
} else { |
||||
curl_setopt($curl, CURLOPT_HTTPGET, true); |
||||
} |
||||
$response = curl_exec($curl); |
||||
|
||||
if ($method == 'HEAD') { |
||||
$headers = []; |
||||
foreach (explode("\n", $response) as $header) { |
||||
$pos = strpos($header, ':'); |
||||
$name = strtolower(trim(substr($header, 0, $pos))); |
||||
$headers[$name] = trim(substr($header, $pos+1)); |
||||
} |
||||
return $headers; |
||||
} |
||||
|
||||
if (curl_errno($curl)) { |
||||
throw new Exception(curl_error($curl), curl_errno($curl)); |
||||
} |
||||
|
||||
return $response; |
||||
} |
||||
|
||||
/** |
||||
* Sends HTTP request. |
||||
* @param string $url request URL. |
||||
* @param string $method request method. |
||||
* @param array $params request params. |
||||
* @return array|string response. |
||||
* @throws \yii\base\Exception on failure. |
||||
* @throws \yii\base\NotSupportedException if request method is not supported. |
||||
*/ |
||||
protected function sendStreamRequest($url, $method = 'GET', $params = []) |
||||
{ |
||||
if (!$this->hostExists($url)) { |
||||
throw new Exception('Invalid request.'); |
||||
} |
||||
|
||||
$params = http_build_query($params, '', '&'); |
||||
switch ($method) { |
||||
case 'GET': |
||||
$options = [ |
||||
'http' => [ |
||||
'method' => 'GET', |
||||
'header' => 'Accept: application/xrds+xml, */*', |
||||
'ignore_errors' => true, |
||||
] |
||||
]; |
||||
$url = $url . ($params ? '?' . $params : ''); |
||||
break; |
||||
case 'POST': |
||||
$options = [ |
||||
'http' => [ |
||||
'method' => 'POST', |
||||
'header' => 'Content-type: application/x-www-form-urlencoded', |
||||
'content' => $params, |
||||
'ignore_errors' => true, |
||||
] |
||||
]; |
||||
break; |
||||
case 'HEAD': |
||||
/* We want to send a HEAD request, |
||||
but since get_headers doesn't accept $context parameter, |
||||
we have to change the defaults.*/ |
||||
$default = stream_context_get_options(stream_context_get_default()); |
||||
stream_context_get_default([ |
||||
'http' => [ |
||||
'method' => 'HEAD', |
||||
'header' => 'Accept: application/xrds+xml, */*', |
||||
'ignore_errors' => true, |
||||
] |
||||
]); |
||||
|
||||
$url = $url . ($params ? '?' . $params : ''); |
||||
$headersTmp = get_headers($url); |
||||
if (empty($headersTmp)) { |
||||
return []; |
||||
} |
||||
|
||||
// Parsing headers. |
||||
$headers = []; |
||||
foreach ($headersTmp as $header) { |
||||
$pos = strpos($header, ':'); |
||||
$name = strtolower(trim(substr($header, 0, $pos))); |
||||
$headers[$name] = trim(substr($header, $pos + 1)); |
||||
} |
||||
|
||||
// and restore them |
||||
stream_context_get_default($default); |
||||
return $headers; |
||||
default: |
||||
throw new NotSupportedException("Method {$method} not supported"); |
||||
} |
||||
|
||||
if ($this->verifyPeer) { |
||||
$options = array_merge( |
||||
$options, |
||||
[ |
||||
'ssl' => [ |
||||
'verify_peer' => true, |
||||
'capath' => $this->capath, |
||||
'cafile' => $this->cainfo, |
||||
] |
||||
] |
||||
); |
||||
} |
||||
|
||||
$context = stream_context_create($options); |
||||
return file_get_contents($url, false, $context); |
||||
} |
||||
|
||||
/** |
||||
* Sends request to the server |
||||
* @param string $url request URL. |
||||
* @param string $method request method. |
||||
* @param array $params request parameters. |
||||
* @return array|string response. |
||||
*/ |
||||
protected function sendRequest($url, $method = 'GET', $params = []) |
||||
{ |
||||
if (function_exists('curl_init') && !ini_get('safe_mode')) { |
||||
return $this->sendCurlRequest($url, $method, $params); |
||||
} |
||||
return $this->sendStreamRequest($url, $method, $params); |
||||
} |
||||
|
||||
/** |
||||
* Combines given URLs into single one. |
||||
* @param string $baseUrl base URL. |
||||
* @param string|array $additionalUrl additional URL string or information array. |
||||
* @return string composed URL. |
||||
*/ |
||||
protected function buildUrl($baseUrl, $additionalUrl) |
||||
{ |
||||
$baseUrl = parse_url($baseUrl); |
||||
if (!is_array($additionalUrl)) { |
||||
$additionalUrl = parse_url($additionalUrl); |
||||
} |
||||
|
||||
if (isset($baseUrl['query'], $additionalUrl['query'])) { |
||||
$additionalUrl['query'] = $baseUrl['query'] . '&' . $additionalUrl['query']; |
||||
} |
||||
|
||||
$urlInfo = array_merge($baseUrl, $additionalUrl); |
||||
$url = $urlInfo['scheme'] . '://' |
||||
. (empty($urlInfo['username']) ? '' |
||||
:(empty($urlInfo['password']) ? "{$urlInfo['username']}@" |
||||
:"{$urlInfo['username']}:{$urlInfo['password']}@")) |
||||
. $urlInfo['host'] |
||||
. (empty($urlInfo['port']) ? '' : ":{$urlInfo['port']}") |
||||
. (empty($urlInfo['path']) ? '' : $urlInfo['path']) |
||||
. (empty($urlInfo['query']) ? '' : "?{$urlInfo['query']}") |
||||
. (empty($urlInfo['fragment']) ? '' : "#{$urlInfo['fragment']}"); |
||||
return $url; |
||||
} |
||||
|
||||
/** |
||||
* Scans content for <meta>/<link> tags and extract information from them. |
||||
* @param string $content HTML content to be be parsed. |
||||
* @param string $tag name of the source tag. |
||||
* @param string $matchAttributeName name of the source tag attribute, which should contain $matchAttributeValue |
||||
* @param string $matchAttributeValue required value of $matchAttributeName |
||||
* @param string $valueAttributeName name of the source tag attribute, which should contain searched value. |
||||
* @return string|boolean searched value, "false" on failure. |
||||
*/ |
||||
protected function extractHtmlTagValue($content, $tag, $matchAttributeName, $matchAttributeValue, $valueAttributeName) |
||||
{ |
||||
preg_match_all("#<{$tag}[^>]*$matchAttributeName=['\"].*?$matchAttributeValue.*?['\"][^>]*$valueAttributeName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1); |
||||
preg_match_all("#<{$tag}[^>]*$valueAttributeName=['\"](.+?)['\"][^>]*$matchAttributeName=['\"].*?$matchAttributeValue.*?['\"][^>]*/?>#i", $content, $matches2); |
||||
$result = array_merge($matches1[1], $matches2[1]); |
||||
return empty($result) ? false : $result[0]; |
||||
} |
||||
|
||||
/** |
||||
* Performs Yadis and HTML discovery. |
||||
* @param string $url Identity URL. |
||||
* @return array OpenID provider info, following keys will be available: |
||||
* - 'url' - string OP Endpoint (i.e. OpenID provider address). |
||||
* - 'version' - integer OpenID protocol version used by provider. |
||||
* - 'identity' - string identity value. |
||||
* - 'identifier_select' - boolean whether to request OP to select identity for an user in OpenID 2, does not affect OpenID 1. |
||||
* - 'ax' - boolean whether AX attributes should be used. |
||||
* - 'sreg' - boolean whether SREG attributes should be used. |
||||
* @throws Exception on failure. |
||||
*/ |
||||
public function discover($url) |
||||
{ |
||||
if (empty($url)) { |
||||
throw new Exception('No identity supplied.'); |
||||
} |
||||
$result = [ |
||||
'url' => null, |
||||
'version' => null, |
||||
'identity' => $url, |
||||
'identifier_select' => false, |
||||
'ax' => false, |
||||
'sreg' => false, |
||||
]; |
||||
|
||||
// Use xri.net proxy to resolve i-name identities |
||||
if (!preg_match('#^https?:#', $url)) { |
||||
$url = 'https://xri.net/' . $url; |
||||
} |
||||
|
||||
/* We save the original url in case of Yadis discovery failure. |
||||
It can happen when we'll be lead to an XRDS document |
||||
which does not have any OpenID2 services.*/ |
||||
$originalUrl = $url; |
||||
|
||||
// A flag to disable yadis discovery in case of failure in headers. |
||||
$yadis = true; |
||||
|
||||
// We'll jump a maximum of 5 times, to avoid endless redirections. |
||||
for ($i = 0; $i < 5; $i ++) { |
||||
if ($yadis) { |
||||
$headers = $this->sendRequest($url, 'HEAD'); |
||||
|
||||
$next = false; |
||||
if (isset($headers['x-xrds-location'])) { |
||||
$url = $this->buildUrl($url, trim($headers['x-xrds-location'])); |
||||
$next = true; |
||||
} |
||||
|
||||
if (isset($headers['content-type']) |
||||
&& (strpos($headers['content-type'], 'application/xrds+xml') !== false |
||||
|| strpos($headers['content-type'], 'text/xml') !== false) |
||||
) { |
||||
/* Apparently, some providers return XRDS documents as text/html. |
||||
While it is against the spec, allowing this here shouldn't break |
||||
compatibility with anything. |
||||
--- |
||||
Found an XRDS document, now let's find the server, and optionally delegate.*/ |
||||
$content = $this->sendRequest($url, 'GET'); |
||||
|
||||
preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m); |
||||
foreach ($m[1] as $content) { |
||||
$content = ' ' . $content; // The space is added, so that strpos doesn't return 0. |
||||
|
||||
// OpenID 2 |
||||
$ns = preg_quote('http://specs.openid.net/auth/2.0/'); |
||||
if (preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) { |
||||
if ($type[1] == 'server') { |
||||
$result['identifier_select'] = true; |
||||
} |
||||
|
||||
preg_match('#<URI.*?>(.*)</URI>#', $content, $server); |
||||
preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate); |
||||
if (empty($server)) { |
||||
throw new Exception('No servers found!'); |
||||
} |
||||
// Does the server advertise support for either AX or SREG? |
||||
$result['ax'] = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>'); |
||||
$result['sreg'] = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); |
||||
|
||||
$server = $server[1]; |
||||
if (isset($delegate[2])) { |
||||
$result['identity'] = trim($delegate[2]); |
||||
} |
||||
|
||||
$result['url'] = $server; |
||||
$result['version'] = 2; |
||||
return $result; |
||||
} |
||||
|
||||
// OpenID 1.1 |
||||
$ns = preg_quote('http://openid.net/signon/1.1'); |
||||
if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) { |
||||
preg_match('#<URI.*?>(.*)</URI>#', $content, $server); |
||||
preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate); |
||||
if (empty($server)) { |
||||
throw new Exception('No servers found!'); |
||||
} |
||||
// AX can be used only with OpenID 2.0, so checking only SREG |
||||
$result['sreg'] = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); |
||||
|
||||
$server = $server[1]; |
||||
if (isset($delegate[1])) { |
||||
$result['identity'] = $delegate[1]; |
||||
} |
||||
|
||||
$result['url'] = $server; |
||||
$result['version'] = 1; |
||||
return $result; |
||||
} |
||||
} |
||||
|
||||
$next = true; |
||||
$yadis = false; |
||||
$url = $originalUrl; |
||||
$content = null; |
||||
break; |
||||
} |
||||
if ($next) { |
||||
continue; |
||||
} |
||||
|
||||
// There are no relevant information in headers, so we search the body. |
||||
$content = $this->sendRequest($url, 'GET'); |
||||
$location = $this->extractHtmlTagValue($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content'); |
||||
if ($location) { |
||||
$url = $this->buildUrl($url, $location); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
if (!isset($content)) { |
||||
$content = $this->sendRequest($url, 'GET'); |
||||
} |
||||
|
||||
// At this point, the YADIS Discovery has failed, so we'll switch to openid2 HTML discovery, then fallback to openid 1.1 discovery. |
||||
$server = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid2.provider', 'href'); |
||||
if (!$server) { |
||||
// The same with openid 1.1 |
||||
$server = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid.server', 'href'); |
||||
$delegate = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid.delegate', 'href'); |
||||
$version = 1; |
||||
} else { |
||||
$delegate = $this->extractHtmlTagValue($content, 'link', 'rel', 'openid2.local_id', 'href'); |
||||
$version = 2; |
||||
} |
||||
|
||||
if ($server) { |
||||
// We found an OpenID2 OP Endpoint |
||||
if ($delegate) { |
||||
// We have also found an OP-Local ID. |
||||
$result['identity'] = $delegate; |
||||
} |
||||
$result['url'] = $server; |
||||
$result['version'] = $version; |
||||
return $result; |
||||
} |
||||
throw new Exception('No servers found!'); |
||||
} |
||||
throw new Exception('Endless redirection!'); |
||||
} |
||||
|
||||
/** |
||||
* Composes SREG request parameters. |
||||
* @return array SREG parameters. |
||||
*/ |
||||
protected function buildSregParams() |
||||
{ |
||||
$params = []; |
||||
/* We always use SREG 1.1, even if the server is advertising only support for 1.0. |
||||
That's because it's fully backwards compatibile with 1.0, and some providers |
||||
advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com */ |
||||
$params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1'; |
||||
if (!empty($this->requiredAttributes)) { |
||||
$params['openid.sreg.required'] = []; |
||||
foreach ($this->requiredAttributes as $required) { |
||||
if (!isset($this->axToSregMap[$required])) { |
||||
continue; |
||||
} |
||||
$params['openid.sreg.required'][] = $this->axToSregMap[$required]; |
||||
} |
||||
$params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']); |
||||
} |
||||
|
||||
if (!empty($this->optionalAttributes)) { |
||||
$params['openid.sreg.optional'] = []; |
||||
foreach ($this->optionalAttributes as $optional) { |
||||
if (!isset($this->axToSregMap[$optional])) { |
||||
continue; |
||||
} |
||||
$params['openid.sreg.optional'][] = $this->axToSregMap[$optional]; |
||||
} |
||||
$params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']); |
||||
} |
||||
return $params; |
||||
} |
||||
|
||||
/** |
||||
* Composes AX request parameters. |
||||
* @return array AX parameters. |
||||
*/ |
||||
protected function buildAxParams() |
||||
{ |
||||
$params = []; |
||||
if (!empty($this->requiredAttributes) || !empty($this->optionalAttributes)) { |
||||
$params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; |
||||
$params['openid.ax.mode'] = 'fetch_request'; |
||||
$aliases = []; |
||||
$counts = []; |
||||
$required = []; |
||||
$optional = []; |
||||
foreach (['requiredAttributes', 'optionalAttributes'] as $type) { |
||||
foreach ($this->$type as $alias => $field) { |
||||
if (is_int($alias)) { |
||||
$alias = strtr($field, '/', '_'); |
||||
} |
||||
$aliases[$alias] = 'http://axschema.org/' . $field; |
||||
if (empty($counts[$alias])) { |
||||
$counts[$alias] = 0; |
||||
} |
||||
$counts[$alias] += 1; |
||||
${$type}[] = $alias; |
||||
} |
||||
} |
||||
foreach ($aliases as $alias => $ns) { |
||||
$params['openid.ax.type.' . $alias] = $ns; |
||||
} |
||||
foreach ($counts as $alias => $count) { |
||||
if ($count == 1) { |
||||
continue; |
||||
} |
||||
$params['openid.ax.count.' . $alias] = $count; |
||||
} |
||||
|
||||
// Don't send empty ax.requied and ax.if_available. |
||||
// Google and possibly other providers refuse to support ax when one of these is empty. |
||||
if ($required) { |
||||
$params['openid.ax.required'] = implode(',', $required); |
||||
} |
||||
if ($optional) { |
||||
$params['openid.ax.if_available'] = implode(',', $optional); |
||||
} |
||||
} |
||||
return $params; |
||||
} |
||||
|
||||
/** |
||||
* Builds authentication URL for the protocol version 1. |
||||
* @param array $serverInfo OpenID server info. |
||||
* @return string authentication URL. |
||||
*/ |
||||
protected function buildAuthUrlV1($serverInfo) |
||||
{ |
||||
$returnUrl = $this->getReturnUrl(); |
||||
/* If we have an openid.delegate that is different from our claimed id, |
||||
we need to somehow preserve the claimed id between requests. |
||||
The simplest way is to just send it along with the return_to url.*/ |
||||
if ($serverInfo['identity'] != $this->getClaimedId()) { |
||||
$returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->getClaimedId(); |
||||
} |
||||
|
||||
$params = array_merge( |
||||
$this->buildSregParams(), |
||||
[ |
||||
'openid.return_to' => $returnUrl, |
||||
'openid.mode' => 'checkid_setup', |
||||
'openid.identity' => $serverInfo['identity'], |
||||
'openid.trust_root' => $this->trustRoot, |
||||
] |
||||
); |
||||
|
||||
return $this->buildUrl($serverInfo['url'], ['query' => http_build_query($params, '', '&')]); |
||||
} |
||||
|
||||
/** |
||||
* Builds authentication URL for the protocol version 2. |
||||
* @param array $serverInfo OpenID server info. |
||||
* @return string authentication URL. |
||||
*/ |
||||
protected function buildAuthUrlV2($serverInfo) |
||||
{ |
||||
$params = [ |
||||
'openid.ns' => 'http://specs.openid.net/auth/2.0', |
||||
'openid.mode' => 'checkid_setup', |
||||
'openid.return_to' => $this->getReturnUrl(), |
||||
'openid.realm' => $this->getTrustRoot(), |
||||
]; |
||||
if ($serverInfo['ax']) { |
||||
$params = array_merge($this->buildAxParams(), $params); |
||||
} |
||||
if ($serverInfo['sreg']) { |
||||
$params = array_merge($this->buildSregParams(), $params); |
||||
} |
||||
if (!$serverInfo['ax'] && !$serverInfo['sreg']) { |
||||
// If OP doesn't advertise either SREG, nor AX, let's send them both in worst case we don't get anything in return. |
||||
$params = array_merge($this->buildSregParams(), $this->buildAxParams(), $params); |
||||
} |
||||
|
||||
if ($serverInfo['identifier_select']) { |
||||
$url = 'http://specs.openid.net/auth/2.0/identifier_select'; |
||||
$params['openid.identity'] = $url; |
||||
$params['openid.claimed_id']= $url; |
||||
} else { |
||||
$params['openid.identity'] = $serverInfo['identity']; |
||||
$params['openid.claimed_id'] = $this->getClaimedId(); |
||||
} |
||||
return $this->buildUrl($serverInfo['url'], ['query' => http_build_query($params, '', '&')]); |
||||
} |
||||
|
||||
/** |
||||
* Returns authentication URL. Usually, you want to redirect your user to it. |
||||
* @param boolean $identifierSelect whether to request OP to select identity for an user in OpenID 2, does not affect OpenID 1. |
||||
* @return string the authentication URL. |
||||
* @throws Exception on failure. |
||||
*/ |
||||
public function buildAuthUrl($identifierSelect = null) |
||||
{ |
||||
$authUrl = $this->authUrl; |
||||
$claimedId = $this->getClaimedId(); |
||||
if (empty($claimedId)) { |
||||
$this->setClaimedId($authUrl); |
||||
} |
||||
$serverInfo = $this->discover($authUrl); |
||||
if ($serverInfo['version'] == 2) { |
||||
if ($identifierSelect !== null) { |
||||
$serverInfo['identifier_select'] = $identifierSelect; |
||||
} |
||||
return $this->buildAuthUrlV2($serverInfo); |
||||
} |
||||
return $this->buildAuthUrlV1($serverInfo); |
||||
} |
||||
|
||||
/** |
||||
* Performs OpenID verification with the OP. |
||||
* @param boolean $validateRequiredAttributes whether to validate required attributes. |
||||
* @return boolean whether the verification was successful. |
||||
*/ |
||||
public function validate($validateRequiredAttributes = true) |
||||
{ |
||||
$claimedId = $this->getClaimedId(); |
||||
if (empty($claimedId)) { |
||||
return false; |
||||
} |
||||
$params = [ |
||||
'openid.assoc_handle' => $this->data['openid_assoc_handle'], |
||||
'openid.signed' => $this->data['openid_signed'], |
||||
'openid.sig' => $this->data['openid_sig'], |
||||
]; |
||||
|
||||
if (isset($this->data['openid_ns'])) { |
||||
/* We're dealing with an OpenID 2.0 server, so let's set an ns |
||||
Even though we should know location of the endpoint, |
||||
we still need to verify it by discovery, so $server is not set here*/ |
||||
$params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; |
||||
} elseif (isset($this->data['openid_claimed_id']) && $this->data['openid_claimed_id'] != $this->data['openid_identity']) { |
||||
// If it's an OpenID 1 provider, and we've got claimed_id, |
||||
// we have to append it to the returnUrl, like authUrlV1 does. |
||||
$this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $claimedId; |
||||
} |
||||
|
||||
if ($this->data['openid_return_to'] != $this->returnUrl) { |
||||
// The return_to url must match the url of current request. |
||||
return false; |
||||
} |
||||
|
||||
$serverInfo = $this->discover($claimedId); |
||||
|
||||
foreach (explode(',', $this->data['openid_signed']) as $item) { |
||||
$value = $this->data['openid_' . str_replace('.', '_', $item)]; |
||||
$params['openid.' . $item] = $value; |
||||
} |
||||
|
||||
$params['openid.mode'] = 'check_authentication'; |
||||
|
||||
$response = $this->sendRequest($serverInfo['url'], 'POST', $params); |
||||
|
||||
if (preg_match('/is_valid\s*:\s*true/i', $response)) { |
||||
if ($validateRequiredAttributes) { |
||||
return $this->validateRequiredAttributes(); |
||||
} else { |
||||
return true; |
||||
} |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Checks if all required attributes are present in the server response. |
||||
* @return boolean whether all required attributes are present. |
||||
*/ |
||||
protected function validateRequiredAttributes() |
||||
{ |
||||
if (!empty($this->requiredAttributes)) { |
||||
$attributes = $this->fetchAttributes(); |
||||
foreach ($this->requiredAttributes as $openIdAttributeName) { |
||||
if (!isset($attributes[$openIdAttributeName])) { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Gets AX attributes provided by OP. |
||||
* @return array array of attributes. |
||||
*/ |
||||
protected function fetchAxAttributes() |
||||
{ |
||||
$alias = null; |
||||
if (isset($this->data['openid_ns_ax']) && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0') { |
||||
// It's the most likely case, so we'll check it before |
||||
$alias = 'ax'; |
||||
} else { |
||||
// 'ax' prefix is either undefined, or points to another extension, so we search for another prefix |
||||
foreach ($this->data as $key => $value) { |
||||
if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_' && $value == 'http://openid.net/srv/ax/1.0') { |
||||
$alias = substr($key, strlen('openid_ns_')); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
if (!$alias) { |
||||
// An alias for AX schema has not been found, so there is no AX data in the OP's response |
||||
return []; |
||||
} |
||||
|
||||
$attributes = []; |
||||
foreach ($this->data as $key => $value) { |
||||
$keyMatch = 'openid_' . $alias . '_value_'; |
||||
if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { |
||||
continue; |
||||
} |
||||
$key = substr($key, strlen($keyMatch)); |
||||
if (!isset($this->data['openid_' . $alias . '_type_' . $key])) { |
||||
/* OP is breaking the spec by returning a field without |
||||
associated ns. This shouldn't happen, but it's better |
||||
to check, than cause an E_NOTICE.*/ |
||||
continue; |
||||
} |
||||
$key = substr($this->data['openid_' . $alias . '_type_' . $key], strlen('http://axschema.org/')); |
||||
$attributes[$key] = $value; |
||||
} |
||||
return $attributes; |
||||
} |
||||
|
||||
/** |
||||
* Gets SREG attributes provided by OP. SREG names will be mapped to AX names. |
||||
* @return array array of attributes with keys being the AX schema names, e.g. 'contact/email' |
||||
*/ |
||||
protected function fetchSregAttributes() |
||||
{ |
||||
$attributes = []; |
||||
$sregToAx = array_flip($this->axToSregMap); |
||||
foreach ($this->data as $key => $value) { |
||||
$keyMatch = 'openid_sreg_'; |
||||
if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { |
||||
continue; |
||||
} |
||||
$key = substr($key, strlen($keyMatch)); |
||||
if (!isset($sregToAx[$key])) { |
||||
// The field name isn't part of the SREG spec, so we ignore it. |
||||
continue; |
||||
} |
||||
$attributes[$sregToAx[$key]] = $value; |
||||
} |
||||
return $attributes; |
||||
} |
||||
|
||||
/** |
||||
* Gets AX/SREG attributes provided by OP. Should be used only after successful validation. |
||||
* Note that it does not guarantee that any of the required/optional parameters will be present, |
||||
* or that there will be no other attributes besides those specified. |
||||
* In other words. OP may provide whatever information it wants to. |
||||
* SREG names will be mapped to AX names. |
||||
* @return array array of attributes with keys being the AX schema names, e.g. 'contact/email' |
||||
* @see http://www.axschema.org/types/ |
||||
*/ |
||||
public function fetchAttributes() |
||||
{ |
||||
if (isset($this->data['openid_ns']) && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0') { |
||||
// OpenID 2.0 |
||||
// We search for both AX and SREG attributes, with AX taking precedence. |
||||
return array_merge($this->fetchSregAttributes(), $this->fetchAxAttributes()); |
||||
} |
||||
return $this->fetchSregAttributes(); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
protected function initUserAttributes() |
||||
{ |
||||
return array_merge(['id' => $this->getClaimedId()], $this->fetchAttributes()); |
||||
} |
||||
} |
@ -0,0 +1,83 @@
|
||||
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'] |
||||
]); ?> |
||||
``` |
@ -0,0 +1,82 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,76 @@
|
||||
<?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'); |
||||
} |
||||
} |
@ -0,0 +1,92 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,90 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,167 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,90 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,90 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,86 @@
|
||||
<?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'; |
||||
} |
||||
} |
@ -0,0 +1,28 @@
|
||||
{ |
||||
"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" |
||||
} |
@ -0,0 +1,51 @@
|
||||
<?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); |
||||
} |
||||
} |
@ -0,0 +1,47 @@
|
||||
<?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)); |
||||
} |
||||
} |
@ -0,0 +1,33 @@
|
||||
<?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; |
||||
} |
||||
} |
@ -0,0 +1,168 @@
|
||||
<?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); |
||||
} |
||||
} |
@ -0,0 +1,38 @@
|
||||
<?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 "<?= Yii::$app->name; ?>"...</h2>
|
||||
<h3 id="link"><a href="<?= $url; ?>">Click here to return to the "<?= Yii::$app->name; ?>".</a></h3>
|
||||
<script type="text/javascript"> |
||||
document.getElementById('title').style.display = ''; |
||||
document.getElementById('link').style.display = 'none'; |
||||
</script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,229 @@
|
||||
<?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'); |
||||
} |
||||
} |
@ -0,0 +1,30 @@
|
||||
<?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', |
||||
]; |
||||
} |
@ -0,0 +1,82 @@
|
||||
.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; |
||||
} |
@ -0,0 +1,68 @@
|
||||
/** |
||||
* 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); |
||||
}); |
||||
}); |
||||
}; |
||||
}); |
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,68 @@
|
||||
<?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); |
||||
} |
||||
} |
@ -0,0 +1,82 @@
|
||||
<?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 |
||||
{ |
||||
} |
@ -0,0 +1,251 @@
|
||||
<?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¶m2=value2', |
||||
], |
||||
[ |
||||
'http://test.url?with=some', |
||||
[ |
||||
'param1' => 'value1', |
||||
'param2' => 'value2', |
||||
], |
||||
'http://test.url?with=some¶m1=value1¶m2=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)); |
||||
} |
||||
} |
@ -0,0 +1,85 @@
|
||||
<?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 |
||||
{ |
||||
} |
@ -0,0 +1,109 @@
|
||||
<?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!'); |
||||
} |
||||
} |
@ -0,0 +1,33 @@
|
||||
<?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!'); |
||||
} |
||||
} |
@ -0,0 +1,61 @@
|
||||
<?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); |
||||
} |
||||
} |
@ -0,0 +1,30 @@
|
||||
<?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(); |
@ -0,0 +1,133 @@
|
||||
<?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!'); |
||||
} |
||||
} |
@ -0,0 +1,50 @@
|
||||
<?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!'); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
<?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!'); |
||||
} |
||||
} |
@ -0,0 +1,20 @@
|
||||
<?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!'); |
||||
} |
||||
} |
@ -0,0 +1,110 @@
|
||||
<?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!'); |
||||
} |
||||
} |
Loading…
Reference in new issue