Commit 68db74a9 by Paul Klimov

OpenId client "authUrl" field added, identity/claimedId processing refactored.

parent f0b9e150
...@@ -192,30 +192,30 @@ class AuthAction extends Action ...@@ -192,30 +192,30 @@ class AuthAction extends Action
} }
/** /**
* @param OpenId $provider provider instance. * @param OpenId $client provider instance.
* @return \yii\web\Response action response. * @return \yii\web\Response action response.
* @throws Exception on failure * @throws Exception on failure
* @throws \yii\web\HttpException * @throws \yii\web\HttpException
*/ */
protected function authOpenId($provider) protected function authOpenId($client)
{ {
if (!empty($_REQUEST['openid_mode'])) { if (!empty($_REQUEST['openid_mode'])) {
switch ($_REQUEST['openid_mode']) { switch ($_REQUEST['openid_mode']) {
case 'id_res': case 'id_res':
if ($provider->validate()) { if ($client->validate()) {
$attributes = array( $attributes = [
'id' => $provider->identity 'id' => $client->getClaimedId()
); ];
$rawAttributes = $provider->fetchAttributes(); $rawAttributes = $client->fetchAttributes();
foreach ($provider->requiredAttributes as $openIdAttributeName) { foreach ($client->requiredAttributes as $openIdAttributeName) {
if (isset($rawAttributes[$openIdAttributeName])) { if (isset($rawAttributes[$openIdAttributeName])) {
$attributes[$openIdAttributeName] = $rawAttributes[$openIdAttributeName]; $attributes[$openIdAttributeName] = $rawAttributes[$openIdAttributeName];
} else { } else {
throw new Exception('Unable to complete the authentication because the required data was not received.'); throw new Exception('Unable to complete the authentication because the required data was not received.');
} }
} }
$provider->setUserAttributes($attributes); $client->setUserAttributes($attributes);
return $this->authSuccess($provider); return $this->authSuccess($client);
} else { } else {
throw new Exception('Unable to complete the authentication because the required data was not received.'); throw new Exception('Unable to complete the authentication because the required data was not received.');
} }
...@@ -228,8 +228,7 @@ class AuthAction extends Action ...@@ -228,8 +228,7 @@ class AuthAction extends Action
break; break;
} }
} else { } else {
//$provider->identity = $provider->authUrl; // Setting identifier $url = $client->buildAuthUrl();
$url = $provider->buildAuthUrl();
return Yii::$app->getResponse()->redirect($url); return Yii::$app->getResponse()->redirect($url);
} }
return $this->redirectCancel(); return $this->redirectCancel();
......
...@@ -17,7 +17,7 @@ use Yii; ...@@ -17,7 +17,7 @@ use Yii;
* @see http://openid.net/ * @see http://openid.net/
* *
* @property string $returnUrl authentication return URL. * @property string $returnUrl authentication return URL.
* @property mixed $identity ??? * @property string $claimedId claimed identifier (identity).
* @property string $trustRoot client trust root (realm), by default [[\yii\web\Request::hostInfo]] value will be used. * @property string $trustRoot client trust root (realm), by default [[\yii\web\Request::hostInfo]] value will be used.
* *
* @author Paul Klimov <klimov.paul@gmail.com> * @author Paul Klimov <klimov.paul@gmail.com>
...@@ -26,6 +26,11 @@ use Yii; ...@@ -26,6 +26,11 @@ use Yii;
class OpenId extends BaseClient implements ClientInterface 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. * @var array list of attributes, which always should be returned from server.
*/ */
public $requiredAttributes = []; public $requiredAttributes = [];
...@@ -53,9 +58,9 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -53,9 +58,9 @@ class OpenId extends BaseClient implements ClientInterface
* @var string authentication return URL. * @var string authentication return URL.
*/ */
private $_returnUrl; private $_returnUrl;
/**
private $_identity; * @var string claimed identifier (identity)
*/
private $_claimedId; private $_claimedId;
/** /**
* @var string client trust root (realm), by default [[\yii\web\Request::hostInfo]] value will be used. * @var string client trust root (realm), by default [[\yii\web\Request::hostInfo]] value will be used.
...@@ -91,27 +96,26 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -91,27 +96,26 @@ class OpenId extends BaseClient implements ClientInterface
} }
} }
public function setIdentity($value) /**
* @param string $claimedId claimed identifier (identity).
*/
public function setClaimedId($claimedId)
{ {
if (strlen($value = trim((String) $value))) { $this->_claimedId = $claimedId;
if (preg_match('#^xri:/*#i', $value, $m)) {
$value = substr($value, strlen($m[0]));
} elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
$value = "http://$value";
}
if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
$value .= '/';
}
}
$this->_identity = $value;
$this->_claimedId = $value;
} }
public function getIdentity() /**
* @return string claimed identifier (identity).
*/
public function getClaimedId()
{ {
/* We return claimed_id instead of identity, if ($this->_claimedId === null) {
because the developer should see the claimed identifier, if (isset($this->data['openid_claimed_id'])) {
i.e. what he set as identity, not the op-local identifier (which is what we verify)*/ $this->_claimedId = $this->data['openid_claimed_id'];
} elseif (isset($this->data['openid_identity'])) {
$this->_claimedId = $this->data['openid_identity'];
}
}
return $this->_claimedId; return $this->_claimedId;
} }
...@@ -234,13 +238,6 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -234,13 +238,6 @@ class OpenId extends BaseClient implements ClientInterface
$name = strtolower(trim(substr($header, 0, $pos))); $name = strtolower(trim(substr($header, 0, $pos)));
$headers[$name] = trim(substr($header, $pos+1)); $headers[$name] = trim(substr($header, $pos+1));
} }
// Updating claimed_id in case of redirections.
$effectiveUrl = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
if ($effectiveUrl != $url) {
$this->_identity = $this->_claimedId = $effectiveUrl;
}
return $headers; return $headers;
} }
...@@ -313,23 +310,6 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -313,23 +310,6 @@ class OpenId extends BaseClient implements ClientInterface
$pos = strpos($header, ':'); $pos = strpos($header, ':');
$name = strtolower(trim(substr($header, 0, $pos))); $name = strtolower(trim(substr($header, 0, $pos)));
$headers[$name] = trim(substr($header, $pos + 1)); $headers[$name] = trim(substr($header, $pos + 1));
/* Following possible redirections. The point is just to have
claimed_id change with them, because get_headers() will
follow redirections automatically.
We ignore redirections with relative paths.
If any known provider uses them, file a bug report.*/
if ($name == 'location') {
if (strpos($headers[$name], 'http') === 0) {
$this->_identity = $this->_claimedId = $headers[$name];
} elseif($headers[$name][0] == '/') {
$parsedUrl = parse_url($this->_claimedId);
$this->_identity =
$this->_claimedId = $parsedUrl['scheme'] . '://'
. $parsedUrl['host']
. $headers[$name];
}
}
} }
// and restore them // and restore them
...@@ -660,8 +640,8 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -660,8 +640,8 @@ class OpenId extends BaseClient implements ClientInterface
/* If we have an openid.delegate that is different from our claimed id, /* If we have an openid.delegate that is different from our claimed id,
we need to somehow preserve the claimed id between requests. we need to somehow preserve the claimed id between requests.
The simplest way is to just send it along with the return_to url.*/ The simplest way is to just send it along with the return_to url.*/
if ($serverInfo['identity'] != $this->_claimedId) { if ($serverInfo['identity'] != $this->getClaimedId()) {
$returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->_claimedId; $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->getClaimedId();
} }
$params = array_merge( $params = array_merge(
...@@ -707,7 +687,7 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -707,7 +687,7 @@ class OpenId extends BaseClient implements ClientInterface
$params['openid.claimed_id']= $url; $params['openid.claimed_id']= $url;
} else { } else {
$params['openid.identity'] = $serverInfo['identity']; $params['openid.identity'] = $serverInfo['identity'];
$params['openid.claimed_id'] = $this->_claimedId; $params['openid.claimed_id'] = $this->getClaimedId();
} }
return $this->buildUrl(parse_url($serverInfo['url']), ['query' => http_build_query($params, '', '&')]); return $this->buildUrl(parse_url($serverInfo['url']), ['query' => http_build_query($params, '', '&')]);
} }
...@@ -720,7 +700,12 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -720,7 +700,12 @@ class OpenId extends BaseClient implements ClientInterface
*/ */
public function buildAuthUrl($identifierSelect = null) public function buildAuthUrl($identifierSelect = null)
{ {
$serverInfo = $this->discover($this->_identity); $authUrl = $this->authUrl;
$claimedId = $this->getClaimedId();
if (empty($claimedId)) {
$this->setClaimedId($authUrl);
}
$serverInfo = $this->discover($authUrl);
if ($serverInfo['version'] == 2) { if ($serverInfo['version'] == 2) {
if ($identifierSelect !== null) { if ($identifierSelect !== null) {
$serverInfo['identifier_select'] = $identifierSelect; $serverInfo['identifier_select'] = $identifierSelect;
...@@ -733,11 +718,13 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -733,11 +718,13 @@ class OpenId extends BaseClient implements ClientInterface
/** /**
* Performs OpenID verification with the OP. * Performs OpenID verification with the OP.
* @return boolean whether the verification was successful. * @return boolean whether the verification was successful.
* @throws Exception
*/ */
public function validate() public function validate()
{ {
$this->_claimedId = isset($this->data['openid_claimed_id']) ? $this->data['openid_claimed_id'] : $this->data['openid_identity']; $claimedId = $this->getClaimedId();
if (empty($claimedId)) {
return false;
}
$params = [ $params = [
'openid.assoc_handle' => $this->data['openid_assoc_handle'], 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
'openid.signed' => $this->data['openid_signed'], 'openid.signed' => $this->data['openid_signed'],
...@@ -751,27 +738,20 @@ class OpenId extends BaseClient implements ClientInterface ...@@ -751,27 +738,20 @@ class OpenId extends BaseClient implements ClientInterface
$params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; $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']) { } 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, // If it's an OpenID 1 provider, and we've got claimed_id,
// we have to append it to the returnUrl, like authUrl_v1 does. // we have to append it to the returnUrl, like authUrlV1 does.
$this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->_claimedId; $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $claimedId;
} }
if ($this->data['openid_return_to'] != $this->returnUrl) { if ($this->data['openid_return_to'] != $this->returnUrl) {
// The return_to url must match the url of current request. // The return_to url must match the url of current request.
// I'm assuing that noone will set the returnUrl to something that doesn't make sense.
return false; return false;
} }
$serverInfo = $this->discover($this->_claimedId); $serverInfo = $this->discover($claimedId);
foreach (explode(',', $this->data['openid_signed']) as $item) { foreach (explode(',', $this->data['openid_signed']) as $item) {
/* Checking whether magic_quotes_gpc is turned on, because
the function may fail if it is. For example, when fetching
AX namePerson, it might containg an apostrophe, which will be escaped.
In such case, validation would fail, since we'd send different data than OP
wants to verify. stripslashes() should solve that problem, but we can't
use it when magic_quotes is off.*/
$value = $this->data['openid_' . str_replace('.', '_', $item)]; $value = $this->data['openid_' . str_replace('.', '_', $item)];
$params['openid.' . $item] = get_magic_quotes_gpc() ? stripslashes($value) : $value; $params['openid.' . $item] = $value;
} }
$params['openid.mode'] = 'check_authentication'; $params['openid.mode'] = 'check_authentication';
......
...@@ -20,17 +20,16 @@ class GoogleOpenId extends OpenId ...@@ -20,17 +20,16 @@ class GoogleOpenId extends OpenId
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function init() public $authUrl = 'https://www.google.com/accounts/o8/id';
{ /**
parent::init(); * @inheritdoc
$this->setIdentity('https://www.google.com/accounts/o8/id'); */
$this->requiredAttributes = [ public $requiredAttributes = [
'namePerson/first', 'namePerson/first',
'namePerson/last', 'namePerson/last',
'contact/email', 'contact/email',
'pref/language', 'pref/language',
]; ];
}
/** /**
* @inheritdoc * @inheritdoc
......
...@@ -20,15 +20,14 @@ class YandexOpenId extends OpenId ...@@ -20,15 +20,14 @@ class YandexOpenId extends OpenId
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function init() public $authUrl = 'http://openid.yandex.ru';
{ /**
parent::init(); * @inheritdoc
$this->setIdentity('http://openid.yandex.ru'); */
$this->requiredAttributes = [ public $requiredAttributes = [
'namePerson', 'namePerson',
'contact/email', 'contact/email',
]; ];
}
/** /**
* @inheritdoc * @inheritdoc
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment