Zend Framework: plugin OpenID

In your project (Questions and answers for programmers) in Zend Framework I need to connect an OpenID and after hours of work I successfully connected the standard sendowski service. I think the class as easy and convenient(like everything in Zend), but as it turned out, this service does not work with OpenId 2.0, Yes, he just did not finish.

A little digging in the source code I confirmed this — Consumer.php * todo OpenID 2.0 (7.3) XRI and Yadis discovery
Then he looked at the bug tracker and found that it weighs for a long time(very) and no hurry to finish. Then I started to look for an alternative. The choice fell on openidenabled.com/php-openid.

Following is an example that will allow those who are only going to connect to do it in about 15 minutes.

Download library openidenabled.com plug it in the php include path, or manually, whatever you like.

That would not do what has been done to me, I took the core component of the framework CakePhp.
Following code is modified for my desired component.

<?php

class OpenidComponent {
private $controller = null;

public function __construct($controller) {
$this->controller = $controller;
define('Auth_Yadis_CURL_OVERRIDE'true);
}

public function authenticate($openidUrl, $returnTo, $realm, $required = array(), $optional = array()) {
if (trim($openidUrl) != ") {
if ($this->isEmail($openidUrl)) {
$openidUrl = $this->transformEmailToOpenID($openidUrl);
}

$consumer = $this->getConsumer();

$authRequest = $consumer->begin($openidUrl);

if (!isset($authRequest) || !$authRequest) {
throw new InvalidArgumentException('Invalid OpenID');
}

if ($authRequest- > shouldSendRedirect()) {
$redirectUrl = $authRequest- > redirectUrl($realm, $returnTo);

if (Auth_OpenID::isFailure($redirectUrl)) {
throw new Exception('Could not redirect to server: '.$redirectUrl->message);
} else {
$this->controller->redirect($redirectUrl);
}
} else {
$formId = 'openid_message';
$formHtml = $authRequest- > formMarkup($realm, $returnTo false , array('id' => $formId));

if (Auth_OpenID::isFailure($formHtml)) {
throw new Exception('Could not redirect to server: '.$formHtml- > message);
} else {
return '<html><head><title>redirect to the page OpenId server</title></head>'.
"<body onload='document.getElementById(\"".$formId."\").submit()'>".
$formHtml.'</body></html>';
}
}
}
}

public function getResponse($currentUrl) {
$consumer = $this->getConsumer();
$response = $consumer- > complete($currentUrl, $this->getQuery());

return $response;
}

private function getConsumer() {
require_once 'Auth/OpenID/Consumer.php';
return new Auth_OpenID_Consumer($this->getFileStore());
}

private function getQuery() {
$query = Auth_OpenID::getQuery();

// unset the url parameter automatically added by app/webroot/.htaccess
// as it causes problems with the verification of the return_to url
unset($query['url']);

return $query;
}

private function isEmail($string) {
return strpos($string '@');
}

private function transformEmailToOpenID($email) {
if (include_once 'My/Auth/Yadis/Email.php') {
return Auth_Yadis_Email_getID($email);

throw new InvalidArgumentException('Invalid OpenID');
}

private function getFileStore() {

require_once 'Auth/OpenID/FileStore.php';

$storePath = Zend_Registry::getInstance()->configuration->openidFileStore;

if (!file_exists($storePath) && !mkdir($storePath,0777)) {
throw new Exception('Could not create the FileStore directory '.$storePath.'. Please check the effective permissions.');
}

return new Auth_OpenID_FileStore($storePath);
}
}


* This source code was highlighted with Source Code Highlighter.


public function openid(){
if (null === $this->_openid) {
require_once APPLICATION_PATH . '/models/openid.php';
$this->_openid = new OpenidComponent($this);
}
return $this->_openid;

}


* This source code was highlighted with Source Code Highlighter.


As parameter pass the controller to call the redirect(openId previous version), but since $this->_redirect(); is a protected method and cannot be called from other classes, I added a wrapper for it in the controller class(it is certainly better to do it through interfaces, but it is a matter of each).

public function redirect($url){
$this->_redirect($url);
}


* This source code was highlighted with Source Code Highlighter.


define('Auth_Yadis_CURL_OVERRIDE',true); — means that you will use file_get_contents and not curl(for discovery). I have curl installed but when working with https, like Google, the library doesn't display any errors, but not working. I'll spend my time, but it is set up that would work, most likely the problem is in my settings or server configuration.

So, how to use it.

Do action in LoginController, which will get after the user has entered his ID in the input box and clicked login.

public openidAction function(){
error_reporting(E_ERROR);
$auth = Zend_Auth::getInstance();
$flashMessenger = $this->_helper- > FlashMessenger;
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
$identifier = trim($this->getRequest()->getParam("openid_identifier"));
$openidComponent = $this->openid();
try{
$ret = $openidComponent->authenticate($identifier,Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/',Zend_Registry::getInstance()->configuration->webhost, $required = array(), $optional = array());
if ($ret){
echo $ret;
}

}catch(Exception $e){

Zend_Registry::getInstance()->logger->ERR("openid error:".$e->getMessage().$e->getTraceAsString());
$flashMessenger- > addMessage("Incorrect openID!");
return $this->_redirect('/login/');
}
}


* This source code was highlighted with Source Code Highlighter.


Zend_Registry::getInstance()->configuration->webhost = is the name of my host, it is possible to get variable from SERVER(but I need it in other places)

Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/ — this is where OpendId server needs to do a redirect after the login(successful or by pressing cancel). You can go back to /login/ with some parameter, and process it, again a personal matter.

Next, the code which checks the login in the return from the OpenId server:

public openidcallbackAction function(){

$openidComponent = $this->openid();
$response = $openidComponent->getResponse(Zend_Registry::getInstance()->configuration->webhost.'/login/openidcallback/');
$flashMessenger = $this->_helper- > FlashMessenger;
if ($response->status == Auth_OpenID_CANCEL) {

$flashMessenger- > addMessage('Verification was cancelled!');
return $this->_redirect('/login/');
} else if ($response->status == Auth_OpenID_FAILURE) {
$flashMessenger- > addMessage("authorization Error: $response->message !");
return $this->_redirect('/login/');
} else if ($response->status == Auth_OpenID_SUCCESS) {

$auth = Zend_Auth::getInstance();
$openid = $response->getDisplayIdentifier();
$model = $this->getUserModel();
//look for user, if not found suggest to choose a userName on site
$user = $model->findByOpenid($openid);
if ($user){
$auth->getStorage()->write($user);

return $this->_redirect("/");//main page
}

$flashMessenger- > addMessage('You first on the website. Please choose a username!');

$model = Lookup::get()->user();
$newUser = $model->create();
$newUser- > save();

$auth->getStorage()->write($newUser);
$openid = $response->getDisplayIdentifier();
$model->addUserOpenidURL($newUser, $openid);
return $this->_redirect('/settings/choosename/');

}

}


* This source code was highlighted with Source Code Highlighter.


Storage Association between a database user and OpenID ID I am using a table. findByOpenid and addUserOpenidURL work with her. I thought that "unknown" or "guest" inappropriate for my site, and if the user is successfully primogenita, but for the first time on the site, I suggest to choose a user name to continue(this paragraph might be superfluous for those who do not believe).

To store OpenId associations and nonces(don't know how to translate better) use file storage, because storage in the database this library material requires a connection to the database from PEAR, and I didn't want unnecessary objects.

I hope the developers of Zend decide to finish implementing OpenId and then I will have to write a data migration from the format of this library in a Zend format, I think it will not create any problems, and can serve as a topic for a separate topic.

Maybe someone made a patch to the zend framework and already successfully utilizes native sendowski services, I would be very grateful if You share them.

If someone is topic useful but not sufficiently detailed, write comments, will try to add.

P. S. please really don't quibble over Naming Conventions, I know I don't really follow :)
Article based on information from habrahabr.ru

Comments

Popular posts from this blog

Powershell and Cyrillic in the console (updated)

Active/Passive PostgreSQL Cluster, using Pacemaker, Corosync

Automatic deployment ElasticBeanstalk using Bitbucket Pipelines