📜 ⬆️ ⬇️

Zend Framework: we connect OpenID

In my project ( Questions and Answers for Programmers ) on the Zend Framework, it became necessary for me to connect OpenID, and after an hour of work I successfully connected a standard Zend Framework service. I think the class is as easy and convenient (as everything is in Zend), but as it turned out this service does not work with OpenId 2.0, yes - it is simply not added.

Having a little rummaged in the source code I confirmed it - Consumer.php * todo OpenID 2.0 (7.3) XRI and Yadis discovery
Then I looked at the bugtracker and it turned out that it weighs for a long time (very) and no one hurries to finish it. Then I began to look for an alternative. The choice came on openidenabled.com/php-openid .

Below is an example that will allow those who are still going to connect to make it 15 minutes.

We download the library from openidenabled.com, connect it to the php include path, or manually, as you like.
')
In order not to do what has already been done before me, I took as a basis the components from the CakePhp framework.
Below is the code of the modified component for my needs.

<?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> OpenId </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 a parameter, we pass the controller to call the redirect (the openId of the previous version), but since $ this -> _ redirect (); This is a protected method and cannot be called from other classes, I added a wrapper for it in the controller class (this is better done, of course, through interfaces, but this is a personal matter for everyone).

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


* This source code was highlighted with Source Code Highlighter .


define ('Auth_Yadis_CURL_OVERRIDE', true); - means that file_get_contents will be used and not curl (for discovery). I have curl installed but when working with https, like Google’s, the library does not produce any errors, but it does not work either. I will spend my time on this, but for now I’ve set it up to work, most likely the problem is in my settings or server configuration.

And so how to use it.

In the LoginController, we do an action, which we will enter after the user has entered his ID in the input field and pressed the login.

public function openidAction(){
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( " 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, you can get it from the SERVER variable (but I need it in other places)

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

Next, the code that checks the login to return from the server OpenId:

public function openidcallbackAction(){

$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( ' !' );
return $ this ->_redirect( '/login/' );
} else if ($response->status == Auth_OpenID_FAILURE) {
$flashMessenger->addMessage( " : $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 userName on site
$user = $model->findByOpenid($openid);
if ($user){
$auth->getStorage()->write($user);

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

$flashMessenger->addMessage( ' . , !' );

$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 .


I use a table to store the association between the user base and the OpenID identifier. findByOpenid and addUserOpenidURL work with it. I found that the “unknown” or “guest” is not suitable for my site, and if the user successfully logged in, but for the first time on the site, I suggest choosing a username to continue (this item may be superfluous for those who do not think so).

To store the OpenId association and nonces (I don’t know how to translate better), the file storage is used, because for storing in the database this file requires a connection to the database from PEAR, but I didn’t want extra objects.

I hope the developers will still decide to finish the implementation of OpenId and then they will have to write the transfer of data from the format of this library to the format of the zend, I think it will not create any special problems, and may serve as a topic for a separate topic.

Maybe someone made their own patches to the zend framework and already successfully uses native zendovskie services, would be very grateful if you share them.

If someone has a useful topic, but not detailed enough, write comments, I will try to add.

PS I ask you not to find fault with the Naming Conventions, I know, I don’t observe them very much :)

Source: https://habr.com/ru/post/54734/


All Articles