29.3. Zend_OpenId_Provider

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.

29.3.1. Quick Start

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>&nbsp;</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.

29.3.2. Combine all together

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.

29.3.3. Simple Registration Extension

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';
    }
}
            

29.3.4. What Else?

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_Extensions or by creating a child class.

    Поддержать сайт на родительском проекте КГБ