Saturday, September 22, 2007

Hands-on experience implementing OpenID

This is my third post about OpenID. To get you started, see my previous posts here and here.
In this post I'll be providing an overview of programming libraries that implement the consumer (a site that enables an OpenID login) and/or identity provider (service/site where a user has registered her OpenID), and also my experiences with them.
For many programming languages an implementation exists (both consumer and server). Libraries are available for: Java, C#, C++, Perl, Python, Ruby, Coldfusion and PHP. For an extensive list of these libraries, see this list.

My experiences with PHP libraries
As I mentioned in my first post, I've been working on a project where I had to add OpenID to an existing site. This site was built in PHP, so I had to look for a consumer library to OpenID-enable the site.
At first I looked at the high-quality opensource OpenID libraries provided by JanRain, Inc, which you can find here. These support also older PHP versions, from PHP 4.3.0 and upwards. The site I had to add OpenID to, still runs on an older PHP version, so this requirement was met. But after trying to integrate the library, I found out that it requires many PHP extensions the customer who the site belongs to, did not want to install all of them. For example PEAR::DB is needed if you use SQLite, PostgreSQL, or MySQL to store the OpenID data. (You might wonder: who does't use PEAR::DB? Well this customer doesnt' :-) Note that you might get away with a FileStore, as mentioned in this EasyOpenID implementation.

So to make the implementation more lightweight (I only needed a consumer supporting OpenID 1.1, preferably w/o any PEAR dependency), I started to look for alternatives. The most lightweight PHP library I could find was the Simple OpenID PHP Class. The only requirement it has is CURL. Basically it is only one PHP class file. It did contain some bugs originally, in the forum of the class you can find the most up-to-date code with a bunch of fixes.
Since the site I had to modify already had existing users, I had to come up with an implementation plan that handles migrating them too. This meant allowing existing users to be able to have an OpenID too. One problem is: how do you associate them with an OpenID? We basically did the same thing as is elaborately described in this nice article from Joseph Smarr, who implemented OpenID for their Plaxo platform. A recommened read if you're about to do the same job!
Basically, the implementation of adding OpenID to an existing site is a significant task. Your users will not see much change from the outside, but internally you most likely will have to modify your login flow, your forgot password flow and your change password flow. Still, providing your users to register with an OpenID is definitely a step forward for the user-friendliness of your site.

Other libraries
My main interest lies in Java, so I was seriously interested in the Java versions of these libraries. I've looked at the code of the OpenId4Java implementation, which originally was created by Sxip and donated to the Google code system.
It supports auto-detection for OpenID version 1.1 and 2.0. If for example the consumer finds out the OpenID version supported by the provider is not 2.0, it will create a client-nonce and append it. Thus a really elaborate library. Check here for a quick introduction btw to see what a nonce is.
But sadly I've not yet been able to integrate or implement one of the Java libraries.
A few note on the Java libraries listed at openid.net:

  • The idprism.org link is dead.
  • The NetMesh site gives quite a few warnings about the libraries being unsupported or in pre-release.
  • As mentioned above, the Sxip library can now be found at Google Code.
  • The "Informed Control Schemat Consumer, AX attribute metadata retriever" is not really a consumer/provider library, but a library for parsing and generating RDF.


Conclusion
I definitely recommend the mentioned PHP class if you only need to build a consumer with OpenID 1.1 support. If you need OpenID 2.0 support, I recommend one of the JanRain libraries. If you don't want to use PEAR::DB, you might be able to get away with this EasyOpenID implementation of a consumer. If you're using CakePHP, I'd recommend checking the OpenID module built for it (I've not tried this library). I've not been able to try out any of the Java classes yet, but I'd definitely recommend checking out the above mentioned quality implementation.

2 comments:

Anonymous said...

Hi, I didn't know how contact you but anyway I hope you read this comment.

It is just some question about OpenId:

I am using the PHP class you suggested and would like to have a login with OpenID without any redirections of the page, so I use AJAX and an iframe for this purpose.

Apart from that, my question is how can I store user identification without any database in a secure way? I only want to store a configuration file for each user, so I would like to avoid database.I hope you just oriented me in the right way.

Thank you very much.

Techie said...

Hey Campbell,

Can't help you on the AJAX version (haven't done that yet), but regarding your other question: when storing usernames in a file you'll definitely lose some security, because a hacker wouldn't need to login to your database anymore to get the usernames, but I assume you know this already :-)

I never did store usernames in files, so can't talk from experience, but what first comes to mind is encrypting your usernames (a one way hash like MD5). Normally you wouldn't do that because then you can't really search on usernames, unless you know the username (which you can then MD5 and search for that in the usernames). As you probably know, the one-way-hash mechanism is normally used for saving non-reversable passwords.

I'd also make sure you protect access to the directory in which you have the usernames file via .htaccess, such that the webserver can't get to it directly (only via the PHP engine). That doesn't stop people who can get to the command line though. Also make sure the read/write permissions on the usernames file are as limited as possible.

Hope this helps a bit,
Regards