Apparently OpenID is all the rage nowadays in the coding world and those using the CodeIgniter framework are left all alone without a proper guide on how to get it working. So as a budding programmer I is be halping all of you. Yes, it IS a long post, but it should help if you read it step by step.
INTRODUCTION
Here is a nifty short guide to get people to use the CodeIgniter OpenID library to start using OpenID in your web application. Firstly I will assume you already have a registration system of some sort, and if you don’t, you should, because not everybody knows/cares about OpenID. What we have to do to spread OpenID is to provide a choice to people, not force them upon it – forcing people would simply get them to leave.
BRIEFING OF SYSTEM
So, let’s say you have a registration system that uses a table to store some sort of user ID (username/email/uid/whatever) and some sort of password. You also have a login form which all works fine, and now you want to allow people to use OpenID on that system.
Here’s the briefing on how it’s going to work. I’m assuming you know how OpenID is used already. I’m also assuming you are comfortable with PHP. What will happen is that in the place of the username field (or if you want you can make a separate form for it) people have the choice to just type in their OpenID URL, then ignore the password field. When they click the login button, firstly we will do a check for whether or not it is an OpenID URL. If it is not valid, then we just continue checking if it’s a proper user account like our previous system and everything is fine. If it IS a valid OpenID URL, then it will perform the usual “please authenticate” checks with the OpenID server, and once that’s done, we’ll add an OpenID row to a NEW OpenID table which’ll store this stuff, and that table will also have a UID column, which allows us to bind an OpenID URL to a user account. Note that this is a one way bind, so many OpenIDs can refer to a single user account, but not the other way around.
This means you will have to create another table in your database, this depends on what type of database you are using, but in general you should have two columns:
Column 1) openid_url
This would be a varchar field to store a canonicalised version of the openid
url, for example http://foo.bar.com/
Column 2) user_id
This would be an INT that would contain the userid of the username this openid
url is binded to. This should be the key field as it must be unique.
Here is the SQL that should create this for you, but remember it only applies on SQL databases.
CREATE TABLE IF NOT EXISTS `user_openids` (
`openid_url` varchar(255) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`openid_url`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Now as a final note, what if a user already has account, but wants an OpenID to bind to that one instead of creating a new one? The simplest way to do this is to automatically create a new one, but have an option, perhaps in the user control panel or settings page that will allow them to “bind” to an existing account. They will have to provide that account’s user/pass to authenticate they are allowed to bind to it, and if they do, then the original account will be deleted, the user_id will change in the user_openids table, and the user will have to relogin to continue. Tada. Simple.
OK, let’s get started.
STEP 1: Setting up the libraries.
Alright, the first step is to get the library. You can obtain it from the CodeIgniter Wiki. Now go and upload all the files. This library is … well … just a CI library, so that means it’s limited to CI functions and so it also needs the actual PHP OpenID Library to actually interface with OpenID itself. So go and download the PHP OpenID Library then upload the Auth/ directory in your system/application/libraries/ directory. Note that this is the same place as the Openid.php file you uploaded from the CI OpenID Library. For those impatient folks who have already run the test controllers, it should’ve failed on you asking for mkdir permissions. Well, instead of chmodding our entire system, we’re just going to add those directories for it ourselves. Go to the CI root directory (the one that contains the system/ directory) and add another directory called tmp/. Go inside tmp/ and then add three more directories called associations/ and nonces/ and temp/. That should be enough to set up the library.
STEP 2: Testing if it works.
The next step, now that you supposedly have the files in the right places, is to check whether or not it works. Thankfully the CI OpenID library has already provided us with a test.php controller. So, what are you waiting for? Go to yoursite.com/test and put in your OpenID URL and see if it authenticates. Obviously if you don’t have an OpenID URL to test with, you need to go get one. There are many providers available. If it works flawlessly (don’t forget to test with a non-valid URL to see if it catches that too) then skip the next paragraph. Or you can read it anyway.
OPTIONAL: If it doesn’t work?
Now, let’s say it’s mucked up for you. If you get nonce already used errors, try clearing out all the files in the tmp/ directory you made earlier on. Other nonces errors or authentication/redirection errors can be a problem with your CI configuration. Go to your system/application/config directory and edit your config.php file. Look for the uri_protocol option and try all of the options there. I find AUTO works fine for me but on my remote server apparently it only likes ORIG_PATH_INFO. Another glitch I encountered on my localhost is that the server would deny my authentication request. This is probably an Apache setting on my localhost but I played so much with my Apache settings it’s probably all my fault. Uploading to my remote server showed it worked perfectly there, but if you’re having problems testing it on your localhost, you might want to apply this very dirty hack. Go to your system/application/libraries/Auth/ directory and edit the OpenID.php file. The very first function is called isFailure($thing). Now screw them but right now I don’t care about authentication because I know it works on a non-mucked up server. So comment out the return is_a($thing… line and add a line after then to just simply return false;. You’ll get:
function isFailure($thing)
{
//return is_a($thing, 'Auth_OpenID_FailureResponse');
return False;
}
Note that I DO NOT RECOMMEND YOU DO THIS. IT IS A HACK IF YOU HAVE PROBLEMS RUNNING IT ON A TESTING SERVER. MAKE SURE YOU KNOW IT WORKS PROPERLY OTHERWISE.
Ok. Assuming now everything is working fine on the test controller, let’s start integrating it into our system.
STEP 3: Port the test controller to your own system.
Now we having a working test controller, but it isn’t part of our own system yet. Let’s say you have another controller which contains a Users class which deals with people logging in. Comment out all the code so that after they try to login using your existing form, it does nothing. Therefore, if your login form points to <form action=”foo/bar”>, you will comment out all the code in bar(). Now we are going to add the OpenID check.
Because a test controller already exists, this speeds up the reverse engineering process significantly as we already have a working example of use. All we have to do is do code reuse in the right places. This is a bit hard to guide because all of our user systems are different in terms of the code. So here you’ll have to use your own wits on if you need modifications to work for your own system.
Here is the system flow chart of how things are going to work – existing processes you should already have on your system are marked in dotted lines:

Firstly we want to look at section (A). This section deals with checking if it is a valid OpenID URL. This can be done by copying the test controller function exactly. I have also added at the beginning some code, so read the comments as to why. Put this code in the function where your login form would normally point to in the <form action=”foo/bar”>. So therefore you would put it in the bar() function. For the moment, we are going to disregard going to section (D), as this is covered later on in Step 4. Here is the code you have to put in bar():
// Before we check if the username is in fact an OpenID, we must secure
// the variable. OpenID refers to the url as $user_id, so be sure to assign
// the value of the inputted openid to $user_id. Here is have used
// $this->username but this might change on your system.
$user_id = trim($this->username);
$user_id = htmlspecialchars($user_id);
// Load the neccessary OpenID libraries as shown by the example.
$this->lang->load('openid', 'english');
$this->load->library('openid');
$this->config->load('openid');
$req = $this->config->item('openid_required');
$opt = $this->config->item('openid_optional');
$policy = site_url($this->config->item('openid_policy'));
$request_to = site_url($this->config->item('openid_request_to'));
$this->openid->set_request_to($request_to);
$this->openid->set_trust_root(base_url());
$this->openid->set_args(null);
$this->openid->set_sreg(true, $req, $opt, $policy);
$pape_policy_uris = array();
$this->openid->set_pape(true, $pape_policy_uris);
$this->openid->authenticate($user_id);
Now, to better integrate into your system (this is optional, but HIGHLY recommended) you should go into your system/application/config directory and edit the openid.php file. Edit it to fit your needs. openid_required is the required information you want to grab from the openid provider, and openid_optional is the optional information you want to grab. That’s all good and fine, but what we’re interested in is the openid_request_to variable. This is the link to the function that checks the authenticity of the openid URL. Right now it goes to the test check function, but I would recreate this check() function in my own controller, as keeping it in a test controller is … stupid.
So if you change the openid_request_to option, you need to recreate the check() function. So it’s practically a copy and paste. Just copy the check() function from the test controller into the controller you want to store the check() function in. Here is the copy and pasted code, with some added comments to show where things happen:
/**
* Checks for proper OpenID authentication.
*/
public function check()
{
$this->lang->load('openid', 'english');
$this->load->library('openid');
$this->config->load('openid');
$request_to = site_url($this->config->item('openid_request_to'));
$this->openid->set_request_to($request_to);
$response = $this->openid->getResponse();
switch ($response->status)
{
// This is probably the most important bit of the check. The switch case
// statements. They're pretty obvious as to what they do. All the same,
// I have bolded part of their names for the hard of thinking ... haha.
case Auth_OpenID_CANCEL:
$data['msg'] = $this->lang->line('openid_cancel');
break;
case Auth_OpenID_FAILURE:
$data['error'] = $this->_set_message('openid_failure', $response->message);
break;
case Auth_OpenID_SUCCESS:
$openid = $response->getDisplayIdentifier();
$esc_identity = htmlspecialchars($openid, ENT_QUOTES);
$data['success'] = $this->_set_message('openid_success', array($esc_identity, $esc_identity), array('%s','%t'));
if ($response->endpoint->canonicalID) {
$data['success'] .= $this->_set_message('openid_canonical', $response->endpoint->canonicalID);
}
$sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
$sreg = $sreg_resp->contents();
foreach ($sreg as $key => $value)
{
$$key = $value;
$data['success'] .= $this->_set_message('openid_content', array($key, $value), array('%s','%t'));
}
$pape_resp = Auth_OpenID_PAPE_Response::fromSuccessResponse($response);
if ($pape_resp)
{
if ($pape_resp->auth_policies)
{
$data['success'] .= $this->lang->line('openid_pape_policies_affected');
foreach ($pape_resp->auth_policies as $uri)
{
$data['success'] .= "<li><tt>$uri</tt></li>";
}
$data['success'] .= "</ul>";
}
else
{
$data['success'] .= $this->lang->line('openid_pape_not_affected');
}
if ($pape_resp->auth_age)
{
$data['success'] .= $this->_set_message('openid_auth_age', $pape_resp->auth_age);
}
if ($pape_resp->nist_auth_level)
{
$data['success'] .= $this->_set_message('openid_nist_level', $pape_resp->nist_auth_level);
}
}
else
{
$data['success'] .= $this->lang->line('openid_pape_noresponse');
}
// At this point, we have authenticated the OpenID COMPLETELY, and
// we have also grabbed whatever information we have requested. For
// example:
// $nickname is the nickname we have grabbed in openid_required. It
// may or may not be empty.
echo $nickname;
if ( !$nickname )
{
// If $nickname is blank, and we require it for our system, we
// should now execute some code that will ask them to fill out a
// nickname. This is for you to figure out. It will probably
// loading a separate view or redirecting.
}
else
{
// If we have all the required information we need, we can move on
// to section (C) in the flow chart.
}
// Remember we had to store the openid URL in a new table so we can
// bind them to users? $esc_identity is the variable you need. Note:
// it is canonicalised - that means that the format is standardized
// such as foo.bar.com becomes http://foo.bar.com/.
echo $esc_identity;
break;
}
$data['pape_policy_uris'] = array(
PAPE_AUTH_MULTI_FACTOR_PHYSICAL,
PAPE_AUTH_MULTI_FACTOR,
PAPE_AUTH_PHISHING_RESISTANT
);
// I have commented out the load->view because we want to use our own
// view. See flow chart. You will have to edit it so that it does what
// YOU want it to do. All I am doing is showing you how to set up a
// basic structure.
//$this->load->view('view_openid', $data);
}
Ok. After you’re sure you know or have a brief idea of what’s going on, also note that several other functions are referenced to. So you’ll also have to add the _set_message() function to this controller. Just copy and paste from the test.php controller. MAKE SURE YOU DO THIS.
Now we have a working check() function. So if we’re on the SUCCESS switch case statement, we now have the requested information we need and an open ID url. The requested information (in the example code above it’s $nickname) can be used in section (B) of the flow diagram, and the canonicalised openid URL can be used in section (C) of the flow diagram. So hooray! We now have a working OpenID system. How you are going to display the form to request for additional information is UP TO YOU on how you are going to add it, as it depends on your system.
STEP 4: Integrate OpenID check with normal usersystem checks.
We’re almost done here. Let’s take another look at section (A) of the flow diagram. Through reverse engineering the OpenID library, we can see that when it checks if it is a valid openid URL, and returns FALSE, it will use the _set_message() function to and give an appropriate error message. This _set_message() function can be found in the system/application/libraries/Openid.php file. If you look at it, it will echo the error message then call an exit(). Now, we don’t want to grudge our users with annoying error messages, especially if there is NOTHING wrong if they don’t provide an openid URL. (Remember, I am assuming you are merging your existing login box with your openID login box. NOT making a new openid login box – if you are making a new login box, you can pretty much disregard this step.) We also don’t want to call an exit(), because we want to continue with section (D). So comment out the echo and the exit line. After commenting them out, we want to replace the exit with something that will tell our script to skip the rest of our OpenID check and go straight to our usual account checks.
Here’s how we do it. (There might be alternatives, but this is what I think is the easiest) We will add a header redirect in place of the exit; line. Here’s what you should get:
function _set_message($error, $msg, $val = '', $sub = '%s')
{
$CI =& get_instance();
$CI->lang->load('openid', 'english');
//echo str_replace($sub, $val, $CI->lang->line($msg));
if ($error)
{
//exit;
header( 'Location: /foo/bar/0' ) ;
}
}
Where foo/bar goes to the same place as your <form action=”whatever”> we talked about in the beginning. Notice I pass a parameter ‘0′ to the function. This is so that we can restructure the existing bar() function to follow this sort of pseudo code:
function bar($check_openid=1)
{
if ( $check_openid )
{
// Here you execute the OpenID check code (NOT THE check() FUNCTION)
// that you added in step 2.
// Note that successful OpenID logins/registrations will occur in
// the check() function, which is elsewhere.
}
if ( !$check_openid )
{
// Now you continue with the code the describes section (D) of the
// flow diagram.
}
}
… and that’s it! You should now have the basis of a system that accepts both traditional user logins and OpenID. Obviously you have a lot of extra to add in but if you’re here anyway you should know how to adapt it to your system and what else you have to add. Particularly in sections (B) and (C). I’m not going to guide you how to do those steps as it depends on what information you want, how you want to display your request form, and how your database is structured. I’m very sorry, but you will have to work it out yourself. (It shouldn’t be too hard)
I have tested this as this is a running documentation based on what I have done to integrate OpenID in my own CI run site. However of course there may be bugs and things I’ve missed out, any comments will be appreciated, and of course I will answer questions (just leave it as a comment)!
Spelling corrections are also welcome.
Please do NOT copy this post. Just link to this page if you want to share it with others.
I hope it has helped! Please leave a comment as I love “thanks yous”.