The Zend_OpenId_Provider
is used to implement OpenID
servers. This chapter provides very basic examples demonstrating how to
build a working server. However, for implementation of a production OpenID
server (like www.myopenid.com) you
may be required to deal with more complex issues.
The following identity includes the code for creating a user account
using Zend_OpenId_Provider::register
. The link element with
rel="openid.server"
points to our own server script. If you
submit this identity to an OpenID-enabled site, it will perform
authentication on this server.
The code before <html> is just a trick that automatically creates a user account. You wont need such code when using real identities.
Пример 29.11. The Identity
<?php require_once "Zend/OpenId/Provider.php"; define("TEST_SERVER", Zend_OpenId::absoluteURL("example-8.php")); define("TEST_ID", Zend_OpenId::selfURL()); define("TEST_PASSWORD", "123"); $server = new Zend_OpenId_Provider(); if (!$server->hasUser(TEST_ID)) { $server->register(TEST_ID, TEST_PASSWORD); } ?> <html><head> <link rel="openid.server" href="<?php echo TEST_SERVER;?>" /> </head><body> <?php echo TEST_ID;?> </body></html>
The following identity server script handles two kinds of requests
from OpenID-enabled sites (for association and authentication). Both of
them are handled by the same method
Zend_OpenId_Provider::handle
. The two arguments to
Zend_OpenId_Provider
are URLs of login and trust pages, these
ask for interaction from the end-user.
On success, the method Zend_OpenId_Provider::handle
returns a string that should be passed back to the OpenID-enabled site. On
failure, it returns false
- in this example it will return a
HTTP 403 response. You will get it if you try to open this page by
web-browser, because it sends a non-OpenID conformed request.
Пример 29.12. Simple Identity Provider
<?php require_once "Zend/OpenId/Provider.php"; $server = new Zend_OpenId_Provider("example-8-login.php", "example-8-trust.php"); $ret = $server->handle(); if (is_string($ret)) { echo $ret; } else if ($ret !== true) { header('HTTP/1.0 403 Forbidden'); echo 'Forbidden'; }
It is a good idea to use a secure connection (HTTPS) for this and especially for the following interactive scripts, to prevent password disclosure.
The following script implements a login screen for an identity
server Zend_OpenId_Provider
and redirects to this page when a
required user has not yet logged-in. On this page, users enter a password
to login.
You should use the password "123" that was used during a tricky user registration from an identity script.
On submit, the script calls Zend_OpenId_Provider::login
with the accepted end-user's identity and password, then redirects it back
to the main identity provider's script. On success, the
Zend_OpenId_Provider::login
establishes a session between the
end-user and the identity-provider and stores the information about
logged-in user. So, all following requests from the same end-user won't
require login procedure (even if they come from another OpenID enabled
web-site).
Note that this session is between end-user and identity provider only. OpenID enabled sites know nothing about it.
Пример 29.13. Simple Login Screen
<?php require_once "Zend/OpenId/Provider.php"; $server = new Zend_OpenId_Provider(); if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['openid_action']) && $_POST['openid_action'] === 'login' && isset($_POST['openid_identifier']) && isset($_POST['openid_password'])) { $server->login($_POST['openid_identifier'], $_POST['openid_password']); Zend_OpenId::redirect("example-8.php", $_GET); } ?> <html><body> <form method="post"><fieldset> <legend>OpenID Login</legend> <table border=0> <tr><td>Name:</td><td><input type="text" name="openid_identifier" value="<?php echo $_GET['openid_identity']; ?>"></td></tr> <tr><td>Password:</td><td><input type="text" name="openid_password" value=""></td></tr> <tr><td> </td><td><input type="submit" name="openid_action" value="login"></td></tr> </table></fieldset></form></body></html>
The fact that the user is logged-in doesn't mean that the authentication must succeed. The user may decide to trust or not to trust particular OpenID enabled sites. The following trust screen allows the end-user to make that choise. This choise may be done only for current requests or "forever". In the last case information about trusted/untrusted sites is stored in an internal database and all following authentication requests from this site will be handled automatically, without user interaction.
Пример 29.14. Simple Trust Screen
<?php require_once "Zend/OpenId/Provider.php"; $server = new Zend_OpenId_Provider(); if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['openid_action']) && $_POST['openid_action'] === 'trust') { if (isset($_POST['allow'])) { if (isset($_POST['forever'])) { $server->allowSite($server->getSiteRoot($_GET)); } $server->respondToConsumer($_GET); } else if (isset($_POST['deny'])) { if (isset($_POST['forever'])) { $server->denySite($server->getSiteRoot($_GET)); } Zend_OpenId::redirect($_GET['openid_return_to'], array('openid.mode'=>'cancel')); } } ?> <html><body> <p>A site identifying as <a href="<?php echo $server->getSiteRoot($_GET);?>"> <?php echo $server->getSiteRoot($_GET);?></a> has asked us for confirmation that <a href="<?php echo $server->getLoggedInUser();?>"> <?php echo $server->getLoggedInUser();?></a> is your identity URL.</p> <form method="post"> <input type="checkbox" name="forever"> <label for="forever">forever</label><br> <input type="hidden" name="openid_action" value="trust"> <input type="submit" name="allow" value="Allow"> <input type="submit" name="deny" value="Deny"> </form></body></html>
Production OpenID servers usually support Simple Registration Extension that allows consumers to ask some information about user from provider. In this case the trust page is usually extended with ability to enter requested fields or to select user profile.
It is possible to combine all provider functions in one script. In
this case login and trust URLs are omitted, and
Zend_OpenId_Provider
assumes that they point to the same page
with additional "openid.action" GET argument.
The following example is not complete. It doesn't provide GUI for end-user like it should, but performs automatic login and trusting instead. It is done just to simplify the example, and real server must include code from previous examples.
Пример 29.15. All together
<?php require_once "Zend/OpenId/Provider.php"; $server = new Zend_OpenId_Provider(); define("TEST_ID", Zend_OpenId::absoluteURL("example-9-id.php")); define("TEST_PASSWORD", "123"); if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_GET['openid_action']) && $_GET['openid_action'] === 'login') { $server->login(TEST_ID, TEST_PASSWORD); unset($_GET['openid_action']); Zend_OpenId::redirect(Zend_OpenId::selfUrl(), $_GET); } else if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_GET['openid_action']) && $_GET['openid_action'] === 'trust') { unset($_GET['openid_action']); $server->respondToConsumer($_GET); } else { $ret = $server->handle(); if (is_string($ret)) { echo $ret; } else if ($ret !== true) { header('HTTP/1.0 403 Forbidden'); echo 'Forbidden'; } }
If you compare this example with previous example divided to
separate page, in addition to dispatch code you will see only the one
difference - unset($_GET['openid_action'])
. This
unset
is necessary to route next request to main
handler.
The following identity page makes a trick again. It creates new user account and associates it with profile (nickname and password). Such tricks aren't needed in real life where end-user registers on OpenID server and fill-in their profiles, but implementing this GUI is not a subject of this manual.
Пример 29.16. Identity with Profile
<?php require_once "Zend/OpenId/Provider.php"; require_once "Zend/OpenId/Extension/Sreg.php"; define("TEST_SERVER", Zend_OpenId::absoluteURL("example-10.php")); define("TEST_ID", Zend_OpenId::selfURL()); define("TEST_PASSWORD", "123"); $server = new Zend_OpenId_Provider(); if (!$server->hasUser(TEST_ID)) { $server->register(TEST_ID, TEST_PASSWORD); $server->login(TEST_ID, TEST_PASSWORD); $sreg = new Zend_OpenId_Extension_Sreg(array( 'nickname' =>'test', 'email' => 'test@test.com' )); $root = Zend_OpenId::absoluteURL("."); Zend_OpenId::normalizeUrl($root); $server->allowSite($root, $sreg); $server->logout(); } ?> <html><head> <link rel="openid.server" href="<?php echo TEST_SERVER;?>" /> </head><body> <?php echo TEST_ID;?> </body></html>
You should pass this identity to OpenID-enabled site (use Simple Registration Extension example from previous chapter) and it will use the following OpenID server script.
It is a variation from previous "All together" example. It uses the
same automatic login mechanism, but it doesn't contain any code for trust
page. The user already trusts "forever" to example scripts. This trust was
made by Zend_OpenId_Provider::alowSite
method in identity
script. The same method associated profile with trusted URL and this
profile will be returned automatically on request from this trusted
URL.
The only thing necessary to make Simple Registration Extension work
is passing object of Zend_OpenId_Extension_Sreg
as second
argument to Zend_OpenId_Provider::handle
.
Пример 29.17. Provider with SREG
<?php require_once "Zend/OpenId/Provider.php"; require_once "Zend/OpenId/Extension/Sreg.php"; $server = new Zend_OpenId_Provider(); $sreg = new Zend_OpenId_Extension_Sreg(); define("TEST_ID", Zend_OpenId::absoluteURL("example-10-id.php")); define("TEST_PASSWORD", "123"); if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_GET['openid_action']) && $_GET['openid_action'] === 'login') { $server->login(TEST_ID, TEST_PASSWORD); unset($_GET['openid_action']); Zend_OpenId::redirect(Zend_OpenId::selfUrl(), $_GET); } else if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_GET['openid_action']) && $_GET['openid_action'] === 'trust') { echo "UNTRUSTED DATA" ; } else { $ret = $server->handle(null, $sreg); if (is_string($ret)) { echo $ret; } else if ($ret !== true) { header('HTTP/1.0 403 Forbidden'); echo 'Forbidden'; } }
Building OpenID servers is less usual tasks then building
OpenID-enabled sites, so this manual don't try to cover all
Zend_OpenId_Provider
features as it was done for
Zend_OpenId_Consumer
.
In two words in additional it provides:
a set of methods to build end-user's GUI interface that allows users to register, manage their trusted sites and profiles.
an abstraction storage layer to store information about users, their sites and profiles. It also stores associations between provider and OpenID-enabled sites. This layer is very similar to the
Zend_OpenId_Consumer
's one. It also uses file storage by default but may be substituted with another implementation.an abstraction user-association layer that may associate end-user's web browser with logged-in identity
Zend_OpenId_Provider
doesn't try to cover all possible
features that can be implemented by OpenID server (like digital
certificates), but it can be easily extended using
Zend_OpenId_Extension
s or by creating a child class.