We are currently working on an OpenID consumer implementation. We looked into the Zend_OpenID - unfortunately it did not work. We are unable to get it work with Google. The remaining choice is the excellent JanRain library.

OK, the Jan rain library is not PHP 5.3 friendly. There are several warnings if you try included consumer test. The library also requires several extensions. To start you’d better read the README first and run the detect.php under its example directory. Fortunately, those warnings are easy to deal with and I won’t repeat them here.

$path = ini_get(’include_path’);
$path = APPLICATION_PATH . ‘/../library/openid-php’ . PATH_SEPARATOR . $path;
ini_set(’include_path’, $path);

require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
require_once "Auth/OpenID/PAPE.php";

class OpenidController extends Zend_Controller_Action
{
  public function init()
  {
    $this->session = new Zend_Session_Namespace(’openid’);
    $this->store = $this->getFileStore();
    $this->consumer = new Auth_OpenID_Consumer($this->store);
  }
  
   /** for testing only. This is not necessary as openid_identifier
  *  is captured in the URL. */
  public function indexAction() 
  {
  }
  
  public function tryAction() 
  {
    $this->openid = $_GET['openid_identifier'];
    try {
      // Make sure the user entered something.
      if(strlen($this->openid) == 0) {
        throw new Exception(’OpenID is empty.’);
      }

      // Try to start an openid authentication.
      $auth_request = $this->consumer->begin($this->openid);

      if(!$auth_request) {
        throw new Exception("No Auth Request");
      }
      
      $sreg_request = Auth_OpenID_SRegRequest::build(
           // Required
           array(’nickname’),
           // Optional
           array(’fullname’, ‘email’));
    
      if ($sreg_request) {
        $auth_request->addExtension($sreg_request);
      }
      

       $redirect_url = $auth_request
        ->RedirectURL($this->getTrustRoot(),$this->getReturnTo());

      if (Auth_OpenID::isFailure($redirect_url)) {
        throw new Exception("Could not redirect to server: "
          . $redirect_url->message);
      }

    } catch (Exception $e) {
      $this->view->error = $e->getMessage();
      $this->render(’index’);
      return;
    }

    $this->_redirect($redirect_url);
  }
    
  private $session;
  private $store;
  private $consumer;
  private $openid;

  public function getFileStore() 
  {
    /**
     * This is where the example will store its OpenID information.
     * You should change this path if you want the example store to be
     * created elsewhere.  After you’re done playing with the example
     * script, you’ll have to remove this directory manually.
     */
    $store_path = null;
    if (function_exists(’sys_get_temp_dir’)) {
      $store_path = sys_get_temp_dir();
    }
    else {
      if (strpos(PHP_OS, ‘WIN’) === 0) {
        $store_path = $_ENV['TMP'];
        if (!isset($store_path)) {
          $dir = ‘C:\Windows\Temp’;
        }
      }
      else {
        $store_path = @$_ENV['TMPDIR'];
        if (!isset($store_path)) {
          $store_path = ‘/tmp’;
        }
      }
    }
    $store_path .= DIRECTORY_SEPARATOR . ‘_atlantis_openid’;
  
    if (!file_exists($store_path) &&
      !mkdir($store_path)) {
      print "Could not create the FileStore directory ‘$store_path’. ".
        " Please check the effective permissions.";
      exit(0);
    }
    $r = new Auth_OpenID_FileStore($store_path);

    return $r;
  }
  
  /**
   * When the identity provider is done having their
   * moment with the user they get returned to this action.
   * Here we look at the response status codes to decide
   * to if they were authenticated or not.
   */
  public function finishAction() 
  {
      $return_to = $this->getReturnTo();
      $response = $this->consumer->complete($return_to);
      
      if($response->status == Auth_OpenID_CANCEL) {
        $this->view->error = ‘Verification cancelled.’;
      } else if ($response->status == Auth_OpenID_FAILURE) {
        $this->view->error = "OpenID authentication failed: "
          . $response->message;
      } else if ($response->status == Auth_OpenID_SUCCESS) {
        $this->view->error =  "Success";
        
        $auth = Zend_Auth::getInstance();
        $adapter = new Application_Model_Account_OpenIDAdapter($response->identity_url);
        
        $result = $auth->authenticate($adapter);
        if ($result->isValid()) {
          $this->_redirect(’/');
          return;
        }
      }
    $this->_redirect(’/account/index’);
  }

  private function getScheme() 
  {
    $scheme = ‘http’;
    if (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == ‘on’) {
      $scheme .= ’s’;
    }
    return $scheme;
  }

  private function getReturnTo() 
  {
    return sprintf("%s://%s/openid/finish",
             $this->getScheme(), $_SERVER['SERVER_NAME']);
  }

  private function getTrustRoot() 
  {
    return sprintf("%s://%s/", $this->getScheme(),
      $_SERVER['SERVER_NAME']);
  }  
  
}

The code starts with /openid/try action. It communicates with OpenID provider to obtain a URL that user will be redirected to. It also set the callback to /openid/finish. Note that I created class Application_Model_Account_OpenIDAdapter so that it fits Zend_Auth architecture. In reality, all it does is to write information to user account database and persist it to a storage in choice (such as session or cookie).