Probably my one favorite aspects of Zend Framework is that you can pull pieces out and use them by themselves without having to build a complete MVC installation. Not that building out a complete installation is difficult but there are times when you just need a small piece, not the entire puzzle. Zend_Auth is one of those pieces that can be pulled out and used by itself. If you need authentication for an existing application Zend_Auth may just be the piece you are looking for.
Since there have been numerous tutorials written regarding Zend_Auth, I won’t attempt to make a full tutorial on it. If you are curious, google around, you’ll find them. Today though, I’d like to show you a cool new piece that has been added to Auth in Zend Framework 1.5, the new Zend_Auth_Adapter_OpenId.
From wikipedia:
OpenID is a decentralized single sign-on system. Using OpenID-enabled sites, web users do not need to remember traditional authentication tokens such as username and password.
Again, this isn’t a tutorial on OpenID, but you will need, at the very least, an OpenID “identifier” to play along. Chances are good that you already have an OpenId even if you don’t know it. AOL, Google, Microsoft and Yahoo are all OpenID providers. Good luck in finding any information on their respective implementations though. I failed to find any information on how to use Google for authentication, my Yahoo OpenID identifier failed to work with the example code (and for this I blame Yahoo) and while AOL has announced support, finding out any concrete data on how to use it. Therefore, I’ve fallen back to myopenid.com. It’s not flashy but it does work and works well. If you have another OpenID identifier, I urge you to try it out with this code and let us know the results.
http://myopenid.com is a good, and free, provider so we’ll stick with it. For the sake of this article, we will assume you either have an OpenID identifier that works or that you have gone out to http://myopenid.com and registered one.
This example does not assume you have a full Zend Framework MVC application. On the contrary, it assumes that you don’t. If you were building an application based on Zend Framework’s MVC, you would most likely be beaten if you tried to do authentication in this manner. The purpose of this code is two-fold. First, to show how easy it is to use OpenID to authenticate and second to show how easy it is to use pieces of Zend Framework inside your existing application.
Like the last “Lifting the Skirt” article, I’m going to give you the entire code in one fell swoop so those of you who like to cut-n-paste can just skip the rest of this. After the code, we will talk a little more about it.
<?php
/*
* Include the needed pieces.
*/
require_once "Zend/Auth.php";
require_once "Zend/Auth/Adapter/OpenId.php";
/*
* Initialize our variables.
*/
$status = "";
$auth = Zend_Auth::getInstance();
/*
* There are 4 possible conditions.
* First Condition
* In this first test, we check to see if we already have an identity. If we
* do and the logout button has not been pressed, we simply display the
* identity. If the logout button HAS been pressed then we clear the
* identity and notify the user.
*/
if ($auth->hasIdentity()) {
if (isset($_POST['openid_action']) &&
$_POST['openid_action'] == "Logout") {
$auth->clearIdentity();
$status = "You are now logged out<br>\n";
} else {
$status = "You are now logged in as ".$auth->getIdentity()."<br>\n";
}
/*
* Second Condition
* The login button was pressed and we have an openid_identifier. Here we
* submit the openID identifier for authentication. This means your page
* will be redirected to the OpenId service provider for authentication. Any
* code after the call to authenticate will be ignored unless something goes
* wrong.
*
* If something does go horribly wrong, we notify the user and ask them to
* look into it.
*/
} else if (isset($_POST['openid_action']) &&
$_POST['openid_action'] == "Login" &&
!empty($_POST['openid_identifier'])) {
$result = $auth->authenticate(
new Zend_Auth_Adapter_OpenId(@$_POST['openid_identifier']));
$status = 'Something went horribly wrong. Please consult your OpenID provider or $deity.'."<br />\n";
/*
* Third Condition
* This processes the callback from your OpenId service provider. This is a
* rather simple check but it's enough to do the job. On the callback, we
* recall authenticate() on the OpenId Adapter. Now it has all of the info
* from your openId service provider and can make a decision as to whether or
* not the authentication passed. If it did not pass, we do a clearIdentity()
* just to make sure there's nothing there. In either case, we gather up any
* messages the OpenID service provider sent and display them to the user.
*/
}else if (isset($_GET['openid_mode']) ||
isset($_POST['openid_mode'])) {
$result = $auth->authenticate(new Zend_Auth_Adapter_OpenId());
if (!$result->isValid()) {
$auth->clearIdentity();
} //if (!$result->isValid())
$status .= implode("<br />\n",$result->getMessages());
/*
* Fourth Condition
* The page is called and the user has not yet logged in. This just lets
* them know.
*/
} else {
$status = "You are not logged in.<br />\n";
}
?>
<html><body>
<script>
<?php echo "$status";?>
<form method="post"><fieldset>
<legend>OpenID Login</legend>
<input type="text" name="openid_identifier" value="">
<input type="submit" name="openid_action" value="Login">
<input type="submit" name="openid_action" value="Logout">
<input type="submit" name="openid_action" value="Test">
</fieldset></form>
</body></html>
First, before we get into a discussion of how things work, please perform a quick check to make sure that they do. If you have your opened identifier handy, go ahead and paste it into the input box and click “Login”. If you are using http://myopenid.com, when you do this, you should be redirected to their login page. (Or if you are already logged in with them then you will be presented with a page that asked you to authorize the login. Clicking on “Accept Once” should return you to your test page.
Now, you have the option of either logging out, which does not interact with your opened provider but will clear the Zend_Auth identity that was stored upon login.
The other button, “Test”, will either display your OpenID identifier if you are logged in, or a message indicating that you are not logged in.
Ok, if you’ve tested and everything works ok then let’s talk about what is actually happening.
Due to the nature of OpenID, the Zend_Auth_Adapter_OpenId is unique among the Zend_Auth_Adapters because you have to call authenticate() method twice. The first time it is called, Zend_Auth_Adapter_OpenId actually makes the call to the OpenID provider. At this point you should see your screen redirected to the provider’s login or authenticate screen. Once you have told your authentication provider that you want to allow (or disallow) the login, you are redirected back to your calling page and the second call to authenticate() is made. This time, no parameters are passed because Zend_Auth_Adapter_OpenId stored off everything it needed before the first call was made. Now it can recall the necessary information, check the status returned by the provider and you can act upon it accordingly. In the case of our simple example, we notify the user of the results. You could however, redirect to a different page or branch your in-page display based on hasIdentity() which will always return true if the login was successful and false if it wasn’t.
In the example above I’ve broken the two calls to Zend_Auth_Adapter_OpenId::authenticate() into two logical clauses for clarity. The example that this was derived from (http://framework.zend.com/manual/en/zend.auth.adapter.openid.html) shows how you could easily make it a single call.
Like everything in Zend Framework, you have many options to help you customize its operation. One of the ways you can customize the operation is by using the Zend_OpenId_Consumer_Storage_File to change where it stores its temporary files. Since we create the adapter twice (blatantly ignoring the DRY principal for the sake of clarity in the example) we have to make two changes to the code above to implement Zend_OpenId_Consumer_Storage_File. The changes are made in the Second and Third conditions.
Second Condition:
} else if (isset($_POST['openid_action']) &&
$_POST['openid_action'] == "Login" &&
!empty($_POST['openid_identifier'])) {
$authAdapter = new Zend_Auth_Adapter_OpenId($_POST['openid_identifier']);
$authAdapterStorage = new Zend_OpenId_Consumer_Storage_File $tempDir);
$authAdapter->setStorage($authAdapterStorage);
$result = $auth->authenticate($authAdapter);
$status = 'Something went horribly wrong. Please consult your OpenID provider or $deity.'."
\n";
Third Condition:
} else if (isset($_GET['openid_mode']) ||
isset($_POST['openid_mode'])) {
$authAdapter = new Zend_Auth_Adapter_OpenId();
$authAdapterStorage = new Zend_OpenId_Consumer_Storage_File($tempDir);
$authAdapter->setStorage($authAdapterStorage);
$result = $auth->authenticate($authAdapter);
if (!$result->isValid()) {
$auth->clearIdentity();
} //if (!$result->isValid())
$status .= implode("
\n",$result->getMessages());
Now the only other two things we need to do is include the new class file and define $tempDir.
/*
* Include the needed pieces.
*/
require_once "Zend/Auth.php";
require_once "Zend/Auth/Adapter/OpenId.php";
require_once "Zend/OpenId/Consumer/Storage/File.php";
/*
* Initialize our variables.
*/
$status = "";
$auth = Zend_Auth::getInstance();
$tempDir = 'c:/web/htdocs/tmp';
Make those changes, and make sure you modify tempDir to point to an existing directory on your filesystem, and you are in business.
I hope this has given you a feel for how easy it is to implement OpenID authentication in your existing application. Working with Zend_Auth and Zend_Auth_Adapter_OpenId is really quite easy whether you are working within a Zend Framework application or not.




February 19, 2008 at 9:22 pm
Thanks for a nice article! It’s cool to see OpenID getting implemented in the Zend Framework.
Anyhow, whilst following along at home, I ran into a snag. I just copied your code onto my dev box, but when I tried to log in using my OpenID, myOpenID farted at me with the following error message:
"The request was not a valid OpenID request. While processing the request, the following error occurred: return_to u’http://localhost/’ not under trust_root u’http:’".
It seems the culprit is Zend_OpenId_Consumer, which uses dirname() to get the root domain of the URL from which the OpenID request is being made. While I have to assume this works fine from any _other_ URL on a site, it blows up from the root–but only if no file is specified: http://localhost/index.php works great.
It was easy to fix, though. I just had to change the line in the "Second Condition" section reading
new Zend_Auth_Adapter_OpenId(@$_POST['openid_identifier']));
to
new Zend_Auth_Adapter_OpenId(@$_POST['openid_identifier'], null, null, ‘http://localhost/’));
and everything works fine. I’m manually specifying the trust_root parameter that gets sent to MyOpenID.
A production site would probably call for a more robust solution, but this quick fix worked for the tutorial.
Or, y’know, just go to http://localhost/index.php instead
February 20, 2008 at 4:54 pm
Well done, and great timing. Yesterday I started researching an OpenID solution for a ZF-based portal application I built. Thank you for the time to write the tutorial, and thanks to all the contributing developers.
Regards,
Steve
March 18, 2008 at 4:43 am
Welcome to the 21st century. They’ve actually let some of us girls go to school now! And some of us even use the computer! And some of us find your title offensive and pathetic. After 10 year in this business I thought we’d all got a clue by now. What’s under your skirt anyway?
April 15, 2008 at 12:58 pm
Yahoo are using the Yadis protocol for their OpenId authentication. As of 1.5.1 Zend Framework does not support the Yadis protocol. Janrain’s OpenID PHP Library is the only PHP OpenID consumer that I know of that supports it. You can download it here http://openidenabled.com/
April 21, 2008 at 5:30 pm
In regards to the woman who was outraged about the title, I just want to point out that many things have skirts and not just women. I for one used to work at a restaurant where skirts were used all the time for tables and they are known to be used for various things as well. So as to flame the author for the title why don’t you think more outside of the box instead of using unintelligent biased outbursts such as the one you have just presented. As for the article, good read
.
June 4, 2008 at 2:13 pm
I’m not one to get offended easily, but honestly the title of this thread did put me off a little. Just because even if he DID mean skirt in some other way than like up a woman’s skirt, it SOUNDS like he’s talking about looking at the Zend Framework’s lady parts, which is a bit strange.
Great article though! I finally have OpenID authentication in my application. I’ve been meaning to implement it for a while now!