Using JanRain OpenID with Zend Framework

Ok, I know Zend_OpenId is on the way, but for those of us who don’t like to wait here’s a way to get OpenID working with Zend Framework. Besides, JanRain’s libraries tend to be pretty solid and up to date so you won’t go wrong using this in place of Zend_OpenId if you choose to in the future.

This implementation is pretty simple. They only thing thrown in is support for saving the last page the user visited on your site so you can return them there once authentication is finished.

I didn’t include any extension support for the simplicity sake, but if you wanted to add Simple Registration or something the examples that come with the JanRain libraries are fairly straightforward.

session = Zend_Registry::getInstance()->get('session');
        $this->store = $this->getFileStore();
        $this->consumer = new Auth_OpenID_Consumer($this->store);
    }

    /**
     * Create a file store object for consumer
     */
    public function getFileStore() {
        $store_path = "/tmp/openid";

        if(!file_exists($store_path) && !mkdir($store_path) ) {
            echo "Could not create file store.";
            exit;
        }

        return new Auth_OpenId_FileStore($store_path);
    }

    /**
     * This is the default action for the controller. Which
     * does two basic things.
     * 1. Records what page the user came from so we can return
     *    them there after login.
     * 2. Renders the index view. (Login form).  Since this is the
     *    index action it will automatically render
     *    views/scripts/login/index.phtml without explicitly calling
     *    $this->view->render('index');
     */
    public function indexAction() {

        // Save the page the user was last at and return them to it
        // after authentication.
        $return_url = parse_url($_SERVER['HTTP_REFERER']);
        if($return_url['host'] == $this->domain) {
                $return = $return_url['path'];
                if(strlen($return_url['query'])) {
                    $return .= "?" . $return_url['query'];
                }
                if(strlen($return_url['fragment'])>0) {
                    $return .= "#" . $return_url['fragment'];
                }
                $this->session->loginReturn = $return;
        }
    }

   /**
    * The login view from the index action posts to this action, then
    * we call consumer->begin() whichs starts the openid login process
    * with the open id from the form post.
    */
    public function tryAction() {
        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");
            }

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

        header("Location: ".$redirect_url);
        $this->render('index');
    }

    /**
     * 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";
                if(isset($this->session->loginReturn) &&
                   strlen($this->session->loginReturn) > 0) {
                    $user = new User($response->identity_url);
                    if($user->isEnrolled()) {

                        // TODO:  Vodoo for setup up a user session and
                        // logging them in.

                        // If the user is enrolled, return them to the page
                        // they visited before the
                        header("Location: " . $this->session->loginReturn);
                        exit;
                    }
                    // The user was logged in successfully, but this is
                    // their first time on your site, so send them through
                    // enrollment.
                    header("Location: /enroll");
                }
            }
        $this->render('index');
    }

    //////////////////////////////////////
    //  HELPER FUNCTIONS
    /////////////////////////////////////

    /**
     * Returns http or https depending on the current protocol
     */
    private function getScheme() {
        $scheme = 'http';
        if (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') {
            $scheme .= 's';
        }
        return $scheme;
    }

    /**
     * Answers the URL we wish for the identity provider to return
     * a user to once the authentication process is finished.
     */
    private function getReturnTo() {
        return sprintf("%s://%s/login/finish",
                       $this->getScheme(), $_SERVER['SERVER_NAME']);
    }

    /**
     * Answers the trust root for this domain.
     */
    private function getTrustRoot() {
        return sprintf("%s://%s/", $this->getScheme(),
         $_SERVER['SERVER_NAME']);
    }
}

?>

Advertisements