Steven Osborn
"I would love to change the world, but they won't give me the source code".

Using JanRain OpenID with Zend Framework

January 28, 2008 – 4:35 pm

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<?php
 
// Make sure these files from the JainRain libraries
// are in your path.
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
 
/**
 * OpenID login controller using JanRain OpenID 2.0  
 * libraries.
 *
 * By: Steven Osborn - http://steven.bitsetters.com
 */                    
class LoginController extends Zend_Controller_Action {
 
    protected $consumer = null;
    protected $store = null;
    protected $domain = 'example.com';
    protected $session = null;
    protected $openid = null;
 
    /**
     * Setup some resources we'll need later for storing 
     * and retrieving data.
     */           
    public function init() {
        $this->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']);
    }
}
 
?>

You must be logged in to post a comment.