How to use CodeIgniter’s OpenID library to integrate OpenID in your existing user system.

On 22 February 2009 with 99 comments

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.

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:

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);

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:

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);

// 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:

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:

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.

Hello Thomas, I have gone to your site and tested with my own OpenID and it has worked fine for me.

However, if you still encounter the check_authentication issues you could try out the hack I suggest in “if it doesn’t work?” Read from “Another glitch I encountered on my localhost is that the server would deny my authentication request. This is probably an…” onwards. I still have to research what exactly the consequences of doing that will do :)

Which browser are you using? Most OpenID sites require cookies to be saved so that might be the issue.

Thomas Traub says: (23 February 2009)

What do You mean by it works for You ? Did You get the green identity confirmation ? Using Your hack on localhost I get it, but that’s not a solution.

I work on Mac OS Leopard, tried with Safari, FF and Opera, all show the same result. . Same with IE, FF under Windows 7 in a VirtualBox.

So, apparently the provider logs in and sets the cookie. The server answer seems not well treated, since changing Browser, deleting Cookies or using a wrong password shows the login form on the second try.

@p.: the benefits of using OpenID are slightly out of the scope of this article, as here I am assuming the user is familiar with the system and wants to know how they can use it.

However, in a nutshell, with OpenID you are able to be identified online by a single URL. This is used in place of a username and password. The benefits are:
1) You store your personal information yourself instead of trusting it to a third party organisation
2) It’s a lot easier to remember.
3) Speeds up registration process and simplifies login systems on OpenID enabled websites.
4) Easier for you to manage than having so many web presences – especially when updating profile information.
5) Less hassle if you want to start using another website that requires a user system.

@Thomas Traub: Congratulations! I’m happy the guide has helped and I’m sure you know how to continue from now :) Thanks for your contributions by the way.

Arnas, this tutorial was about the other way around sorry ;) Perhaps next time.

Do you want to do a public openID provider that many people can sign up for, or just for yourself?
You might be interested in PHPMyID (Google it) but that only works on non hardened PHP installs. I am running on PHP Suhosin so I cannot run it :(

Good luck :)

arnas risqianto says: (28 February 2009)

moult, thanks for your response.
i want to do a public openID provider that many people can sign up for.
i tried using http://openidenabled.com/php-openid/ because it is stable version.

Arnas, though I haven’t yet played with the possibility of creating an OpenID provider with CodeIgniter, I would probably suggest that perhaps a mod_rewrite with a .htaccess file would be sufficient to give those URLs.

I’ll do some research into this as I personally would also like to become a provider. I’ll let you know how things go (well, I’ll post about it on this blog if I find out).

@Matt Reider: Yes the login method and the logout method are all still up to your existing system, and you may do it however you want – cookies, session, etc. This simply acts as a sort of “pre-validation” before your normal user registration/login system takes over.

@Julian: I get a 404 error from your Google link. However assuming Google kept to the OpenID specification (Google does have quite a bad habit of tweaking things unfortunately) it should work, and if it doesn’t it should be quite minor to update it so it does.

Hi Dion, thanks for your response.
The end bracket broke the link, sorry. I have an app that need to integrate with Google OpenID, just curious if anyone have tried it before. Probably I will try it during the week and will share with you any results or questions.

Upon looking at the page I realise that unfortunately no, this will require modifications to work with Google accounts. This is because OpenID should work with a service URL, but Google (again, they frustratingly love tweaking stuff) here has implemented another layer to allow people to use their familiar account details, then go through the Google servers to create a service URL … all of which will be transparent to the user.

As per the link you provided, you will have to implement steps 1, 2, 3, 4, that Google describes, but once you have the endpoint address, with a couple quick edits here and there I don’t see why not the tutorial above could help speed up the process. After all, it’s still using OpenID.

Of course though it’s never as simple as it sounds, so good luck with your web application!

One question that probably you already know (and I still wondering about). Do you know if I have my web application with Google OpenID support, when the user signs in using Google OpenID, it automatically is logged to other Google services such as email, for example?

Nick, I have no experience with OSX nor with MAMP – but I would suggest you quickly dump it on a live server running on Linux and see if it works. If it does I would simply manually override the PAPE issues (just reverse engineer to see what is causing it – then just `return true` or something) when using it on you MAMP as the production environment is likely to be Linux anyway.

If your production environment is going to be OSX then sorry I have no other suggestions.

Well taking away the @ simply supresses the error, which isn’t much use at all. I think the error mainly lies with if you are loading the libraries in step 1 properly. You need to find a way to check whether nor not those are loaded properly.

Alex says: (7 November 2009)

This is the only article in the whole Internet completely describing how to integrate OpenID to the existing registration system on CI. Thank you, thank you, thank you!!!

http://failnation.e2-productions.com/ is one implementation I’ve done from this method. Look at the box “Log in or Register” around the top right. You may type in the OpenID URL in the top input box (the one next to the tos link). You may leave the password field below it blank and press “Sign in”.

And no, I don’t have much experience with WAMP. Perhaps you might want to check your compiler flags if you’re compiling PHP?

http://wipup.org/ is the full site (in progress) which has a modified implementation such that it works on Kohana (a CodeIgniter derivative).

Have you tried running just the test controller by itself? If that doesn’t work, then there’s something fundamentally wrong with your setup.

ChazUK says: (12 November 2009)

Thanks for the fast reply Dion, I tried using my gmail account to login to that site, but it said my username had to be smaller.

I meant a basic ‘download & test’ one so that I can see if it’s my server setup or just me being an idiot.

Whoops! The length check must’ve kicked in before the OpenID could do it’s magic. Also as a note you Google’s implementation of so-called “OpenID” isn’t like the others. To implement a Google login the method I described in my post is inapplicable. See my discussion with Julian in the comments above.

I would also recommend uploading your setup to a LAMP setup – that should determine where the problem is.

I’ve emailed you a fresh CI installation with step 1 implemented. Run the test controller and see if it works :) (if you run into perm issues just chmod as necessary)

Theo, Google does not follow the same original OpenID specifications, as discussed above. Nor does Facebook or Yahoo as far as I know. Unfortunately further work is needed to incorporate these “providers”.

Try with a wordpress account, myopenid, etc.

VikR says: (30 December 2009)

Update – after installing it on my web host (as opposed to working on my development system) – it seemed to start working. :)

Chris says: (5 January 2010)

@Theo – You need to allow “? and &” in the codeigniter URL (in your config) and also remove the ? from .htaccess if you are using it (look for index.php? and remove the ?. This works for me anyway, with no side-effects.

I am also not getting any user info back, this line:
$sreg = $sreg_resp->contents();
akways seems to be null. Anyone having this problem?

I’ve been trying it with vanilla openid hosts, such as MyOpenID, WordPress, or MyID, etc. Can you quickly create an account on one of those and see if it works with those?

As for the Cake component as far as I know it has nothing to do with the CI libraries I used in this.

Also, like VikR, perhaps you could upload it to a live server and see if it works there? It could be a localhost specific problem.

Chris says: (5 January 2010)

They all seem to authenticate – Google,Wordpress,Yahoo and the test controller says that the id has been verified. Just no nickname or other data as far as I can tell.

Actually if you look at the Wiki page the code is attributed to the same link as above – I think I read that someone had converted it over for use in CI. This is just the base openid.php in the libraries folder.

Unfortunately I don’t think Zend OpenID supports Google or Yahoo, just straight openid providers that give you a URL.

Chris, sorry I’m stumped as well. The only thing I have left to suggest is that if you’re not running this on a live server go and put it up – localhost settings may be a bit quirky or at least your own installation of PHP might not have the right stuff compiled in (but then again, most of those normally should produce errors).

That’s interesting – not often I see something that breaks with newer PHP versions :) Thanks for sharing it.

Marnix says: (24 February 2010)

Since you love it so much, a big “thank you”… You made it even more easy to implement the openid library. And also a “thank you” to all the people who left a comment with their tips!

tuanka says: (8 April 2010)

Thanks for your post! However, i got these problems when implementing openid login function with CodeIgniter’s OpenID library intergrated:

– Does any one know how to get email address with response from google? i could only get the “nickname” even though i tried the line of code below within openid.php file in config directory c “$config[‘openid_required’] = array(‘nickname’,’email’);”.

– Secondly, when i tried to login with yahoo openid, i’ve always receive this message “Authentication error; not a valid OpenID”.

Hello tuanka,
As mentioned before Google runs a slightly different implementation of OpenID and I won’t be surprised if Yahoo does too. In fact, I’m quite surprised it managed to get the nickname from Google. In fact, I don’t understand _how_ it even asked for a nickname – Google’s (and Yahoo’s) requires both an email _and_ a password, whereas this tutorial only allows for single-URL identifiers. What did you use as your identifier string for each one?

tuanka says: (10 April 2010)

Firstly, i want to thank you for your answer, Dion. Actually, i did not do anything special but exactly whatever i got from OpenidLibrary and to follow your tutorial. One thing to remind you is that, when i tested the site of Thomas, it worked the same way as mine except Thomas’ example can do with Yahoo openid too, that’s what my example keep saying “Authentication error; not a valid OpenID”.

I’ve got another matter that i need your help. This problem happened when i tested with myopenid and the others(not google or yahoo), i got the message “OpenID authentication failed: Nonce already used or out of range”. What is that problem as i’ve been seaching with google and bing then got stuck without any clue. Please help!!! Thanks all of you!

Sorry for the late reply – I don’t quite understand. Yahoo, Google and the like’s implementation of OpenID require an indentifier as well as a password, not just a standalone URL like other providers. However in this tutorial it only supports a single input – for the standalone URL. So what did you input to test for Yahoo openid?

As for the nonce already used or out of range issue, check the timestamp settings on your server. Make sure it is synchronised properly. It sounds like the nonce isn’t cleared before reauthenticating. Did you try wait a couple minutes between reauthentication attempts?

Hi Dion! thank you for your reply! to your question, i did not do anything special with google openid login. I just used the single URL like i did with other openid providers.

As for the nounce already userd or out of range problem, i’ve already tried what you recommeded but still got the same result.

Does anyone here have any idea to implement openid login funtion with yahoo or google, please make a post here, any help would be appreciated!

Bryan S. says: (17 April 2010)

I just followed this tutorial and used my Google OpenID to verify it. When I am logged out of Google, I simply enter my google URL, and then I am redirected to a Google page and asked for my password. The Google page says something for the effect “SomeDomain.com wants to reguest more information”. I put in my password to login, and then it redirects me back to my site and the openid success page.

I didnt change anything for it to work like this.. it just did. It would be nice however to get the nickname and email from google however.

tuanka says: (18 April 2010)

Surprised! it does not wok for me. Bryan, it’s great to put you code here so everyone can save a lot of work. I think that, not only i am but so many others have been struggling with this problem. Thanks.

Quick addition – Karnen’s first link (on Trac) seems to be broken now so for those interested in exactly what was causing those errors can read here: http://wipup.org//updates/view/104/ (it’s a Kohana system instead of CodeIgniter but it uses the same OpenID implementation)

When I try enter my OpenID url and hit tell it to verify I get a blank page.

I tried integrating the same library myself and hit the same issue, and I tried figuring out what line was causing the problem but it seems like each attempt I would get stuck on a different line.

I’m testing on a development workstation running Ubuntu 10.10 I have installed the php-curl package. I’m a former Unix/Linux system administrator turned programmer, so I can fairly easily follow instructions if people have ideas.

I’m just REALLY frustrated as it seems very inconsistent in where it fails.

William, this means your script is stopping somewhere before finishing. Insert a die() statement to check whether or not execution is still working as expected up to the die() point. If it is, follow the path of the code and move the die() statement as appropriate until you find the exact area which it fails in.

Also, try dumping the files on a live server. It is likely to be something wrong with your setup.

I don’t know CI 2.0, as I haven’t used CI in a while now (switched to Kohana). HMVC will probably mean you will place the libraries and functions in different locations and link them together differently, but the tutorial still applies in terms of principle.

scoohh – we need an error before we can attempt to debug your problem. Please increase your PHP error level (ie, show warnings) so that we can see if you are missing any libraries at the PHP level first – because that will be the most likely cause as you seem to be running the test script provided by the OpenID library.

scoohh says: (13 April 2011)

@Dion Moult – there is no error or warning that shows up. i followed steps 1 & 2 and when i’m about to test it, it doesn’t work.

Leave a Comment

Born in 1992 (do the math), Male, and floating around the GMT+8 timezone.
I do graphic design, some 3D work, and I'm currently working on a "soon" to be released 3D animated movie. I do some programming here and there and have become fluent in XHTML, CSS, PHP and MySQL. Web development is my main area of computer expertise but I always have some weird and fantastic projects being juggled around my schedule. I like the concept of open-source and run Gentoo Linux. I play the piano and compose music. I hate this blog design but I'm never happy with any design. I am currently a student and working part time as a webdeveloper.