Zend_OpenId_Provider
Zend_OpenId_Provider can be used to implement OpenID
servers. This chapter provides examples that demonstrate how to
build a very basic server. However, for implementation of a production OpenID
server (such as » www.myopenid.com) you
may have to deal with more complex issues.
Quick start
The following example includes 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 the <html> tag is just a trick that automatically
creates a user account. You won't need such code when using real
identities.
<?php
// Set up test identity
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>
</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 the
Zend_OpenId_Provider constructor are URLs of
login and trust pages, which ask for input 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. This example will return an
HTTP 403 response if
Zend_OpenId_Provider::handle fails. You will get this response if
you open this script with a web browser, because it sends a non-OpenID conforming
request.
Example #2 Simple Identity Provider
$server = new Zend_OpenId_Provider("example-8-login.php",
"example-8-trust.php");
$ret = $server->handle();
} else if ($ret !== true) {
header('HTTP/1.0 403 Forbidden');
}
Note:
It is a good idea to use a secure connection (HTTPS) for these scripts-
especially for the following interactive scripts- to prevent password
disclosure.
The following script implements a login screen for an identity
server using Zend_OpenId_Provider and redirects to this page when
a required user has not yet logged in. On this page, a user will enter his password
to login.
You should use the password "123" that was used in the identity script above.
On submit, the script calls Zend_OpenId_Provider::login
with the accepted user's identity and password, then redirects back
to the main identity provider's script. On success, the
Zend_OpenId_Provider::login establishes a session between the
user and the identity provider and stores the information about
the user, who is now logged in. All following requests from the same user won't
require a login procedure- even if they come from another OpenID enabled
web site.
Note:
Note that this session is between end-user and identity provider
only. OpenID enabled sites know nothing about it.
Example #3 Simple Login Screen
<?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 htmlspecialchars($_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 now logged in doesn't mean that the
authentication must necessarily succeed. The user may decide not to trust
particular OpenID enabled sites. The following trust screen allows the
end user to make that choice. This choice may either be made only for current
requests or forever. In the second 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.
Example #4 Simple Trust Screen
<?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 htmlspecialchars($server->getSiteRoot($_GET));?>">
</a>
has asked us for confirmation that
<a href="<?php echo htmlspecialchars($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 the Simple Registration
Extension that allows consumers to request some information about the user from
the provider. In this case, the trust page can be extended to allow
entering requested fields or selecting a specific user profile.
Combined Provide Scripts
It is possible to combine all provider functionality 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 the additional "openid.action" GET argument.
Note:
The following example is not complete. It doesn't provide GUI code for
the user, instead performing an automatic login and trust relationship instead.
This is done just to simplify the example; a production server should include some
code from previous examples.
Example #5 Everything Together
$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();
} else if ($ret !== true) {
header('HTTP/1.0 403 Forbidden');
}
}
If you compare this example with previous examples split in to
separate pages, you will see only the one
difference besides the dispatch code:
unset($_GET['openid_action']). This call to unset
is necessary to route the next request to main handler.
Simple Registration Extension
Again, the code before the <html> tag is just a trick to demonstrate
functionality. It creates a new user account and associates it with a profile (nickname
and password). Such tricks aren't needed in deployed providers where end users register
on OpenID servers and fill in their profiles. Implementing this GUI is out of scope for
this manual.
Example #6 Identity with Profile
<?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>
</body>
</html>
You should now pass this identity to the OpenID-enabled web site (use the Simple
Registration Extension example from the previous section), and it should use the
following OpenID server script.
This script is a variation of the script in the "Everything Together" example. It uses
the same automatic login mechanism, but doesn't contain any code for a trust
page. The user already trusts the example scripts forever. This trust was
established by calling the Zend_OpenId_Provider::allowSite()
method in the identity script. The same method associates the profile with the trusted
URL. This profile will be returned automatically for a request from
the trusted URL.
To make Simple Registration Extension work, you must simply
pass an instance of Zend_OpenId_Extension_Sreg as the second
argument to the Zend_OpenId_Provider::handle() method.
Example #7 Provider with SREG
$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') {
} else {
$ret = $server->handle(null, $sreg);
} else if ($ret !== true) {
header('HTTP/1.0 403 Forbidden');
}
}
Anything Else?
Building OpenID providers is much less common than building
OpenID-enabled sites, so this manual doesn't cover all
Zend_OpenId_Provider features exhaustively, as was done for
Zend_OpenId_Consumer.
To summamize, Zend_OpenId_Provider contains:
-
A set of methods to build an end-user GUI that allows
users to register and manage their trusted sites and profiles
-
An abstract storage layer to store information about users,
their sites and their profiles. It also stores associations between
the provider and OpenID-enabled sites. This layer is very similar
to that of the Zend_OpenId_Consumer class. It also uses
file storage by default, but may used with another backend.
-
An abstract user-association layer that may associate
a user's web browser with a logged-in identity
The Zend_OpenId_Provider class doesn't attempt to cover all
possible features that can be implemented by OpenID servers, e.g. digital
certificates, but it can be extended easily using
Zend_OpenId_Extensions or by standard object-oriented extension.