Sample Rails app: multi-OpenIDs per user
Last time, on “Dr Nic loves OpenID”…
Dr Nic had watched a video by Simon Willison glorifying the delights of OpenID to some Googlers.
Dr Nic decided that each User might like to link multiple OpenIDs to their application account.
Of course, if a user wanted multiple accounts then they could use their different OpenIDs to do that too.
All Dr Nic had to do now, was write some code…
In the mean-whilst…
Later that very same day, Dr Nic reads a tutorial by Joseph Smarr at Plaxo - A Recipe for OpenID-Enabling Your Site. He is joyed that Joseph had said the same thing:
It’s a many-to-one relationship (each user can have multiple OpenIDs attached to their account,
but a given OpenID can only be claimed by a single user)
“Champion!” Exclaims Dr Nic in the seclusion of his underground lair 300m isolated tower kitchen.
This article also lays out an implementation plan. It is in excruciating detail, including table schemas, stylesheet snippets and form suggestions.
Dr Nic rubbed his hands together and gets busy…
[/end of 3rd person]
Sample app
As a developer, the only reason you wouldn’t want to support “multiple OpenIDs per User” is because it is a PITA to implement, in that its an administrative bonus feature for your app. Its not really adding any real uber value.
So I followed his instructions - more or less - an have created a sample app. The README includes a demonstration of the app. You can also download it as a ZIP or TAR bundle.
Now you can add multiple OpenIDs per user and be lazy at the same time.
The app builds on top of Ben Curtis’ OpenID sample app, and uses a variation of Ryan Bates’ Railscasts theme, because its sexy.
NOTE: The code is built for Rails Edge, but the two bundles do not include rails edge, so you need to rake rails:freeze:edge after unpacking it (all this and more in the README). If you want to work with Rails 1.2.3 gems, then.. um, I didn’t write a list of changes, but it might still work, I haven’t tested it. Ben’s original app was built for 1.2.3, so at its very heart it should still work. Perhaps.
Deviations from the Joesph’s tutorial
I want my users to be able to throw their OpenID around on any old page where they see an OpenID field.
That is, allow users to login or register with OpenID from either the “login” form or the “registration/signup” form. Within Rails, these are traditionally two separate controllers (sessions and users respectively).
So, I extracted out this functionality in to a module (helper user_openids_helper) and its used by the two controllers.
How’s it work?
Same way that Ben’s sample app worked, more or less, so read his article a couple times, then read through the source code for the multi-OpenID sample app.
I guess a couple tourist highlights might be:
app/helpers/user_openids_helper.rb- the extract fancy codeconfig/routes.rb- we need more routes to support alternate callbacks from OpenID providerssessions_controller.rbandusers_controller.rb- they are emptier and cleaner nowtest\functionals\sessions_controller.rb- at the bottom are some test cases for the login/registration features ofuser_openids_helper
So, grab your Code Camera…
Magic Wiggly Lines => GuessMethod, by Chris Shea
If you ever make time to code just for pleasure, then method_missing and const_missing are just begging for abuse.
Chris Shea has come up with GuessMethod - a very cool hack that now deprecates my concept of Magic Wiggly Lines - a spell-checker for runtime code.
What’s it do? Cop a squiz at this genius…
$ gem install guessmethod -y $ irb > require 'rubygems' > require 'guessmethod' > class Object; include GuessMethod; end # though this could go in the guessmethod.rb file in the gem > class Product; def name; "Some product"; end; end > Prodct.nw.nae attention: replacing non-existant constant Prodct with Product for Object attention: sending new instead of nw to Product:Class attention: sending name instead of nae to #<Product:0×144ff10>:Product => “Some product”
That’s going straight into my .irbrc file. My bad spelling, coupled with my British/Australian English, will never slow me down again!
UPDATE: actually, it doesn’t like being in the .irbrc file for Rails console; so in the config/environments/development.rb files will have to do for the moment.
One App, One User Account and Multiple OpenIDs
Summary: Its the future, and its not Facebook. Learn it.
I’ve just implemented OpenID sign-ons for a client site, as a compliment for the standard signup/login process, and I choose the following association:
1 x User account —> 0 or 1 x OpenID
The OpenID value is a field on my User model/table.
So I login with my OpenID and I get one application account.
Or similarly, if the User already has an account, there is a field on their User settings page for their OpenID. They can put in their LiveJournal or AOL OpenID URL (or from one of 3000+ OpenID providers) there, and they can now log into that account using either normal login, or OpenID login.
Great.
But then I was watching a GoogleTechTalk video by Simon Willison and he gave the following Bonus Use of OpenID:
- User logs into a site using an AOL OpenID
- The site can now send AOL IM messages to that user
This is cool for two reasons:
- The site automagically derived information about the User - they are an AOL member, and their AOL username.
- More importantly, it KNOWS the user is the owner of that AOL account.
The site gets authentication of this information for free through the OpenID sign-in process - the user is redirected back to AOL’s OpenID page at which time the user has to prove they own the account thru AOL signin (or cookies).
So, back to my story.
My users can sign into my site with an AOL OpenID and prove they own an AOL IM account name.
What if they also have a LiveJournal account? LiveJournal URLs are all OpenID URLs too [1]
If they signed in with LiveJournal OpenID then they could prove they have such an account and my site could do funky LiveJournal specific things… like… read the user’s blog for them… ok, this example is going nowhere.
But! What is your MSN/Live account had an OpenID associated with it? Or Google Account? Or Yahoo Account? All have IMs associated with them. OpenID login could prove ownership of that information.
But…
My user has already logged in with AOL OpenID.
Stupid 1-to-1 data model of User and OpenID. Bah!
Solution: allow Users to have 0+ OpenIDs. Some quick refactoring and you’re done.
Your controller code (the standard Rails solutions for OpenID support use a sessions controller to manage the OpenID provider interactions will now have to do a small amount of extra work.
Small.
Like, you’ll need a table of known OpenIDs and a belongs_to foreign key to the User model/table.
Small.
But perhaps you are already doing this and I’m the only silly sausage around here.
Even if you don’t see the benefit of these use cases - trusting the information from the OpenID profile - here’s a more common use case I think we’ll find:
Users will want to sign-in with whichever OpenID makes them feel the happiest at the time.
I’m feeling some AOL love today, I’ll use http://openid.aol.com/iamawesome
I’ll use iamawesome.myopenid.com here as its got my Age and Country setup already.
And the poor user will instantly get 2 accounts with your application - on top of the account they already had. That’s 3 accounts.
Unless we do the following:
- Allow “new” OpenID sign-ins to select an existing application User account to connect to - don’t make the poor user feel stupid for using OpenID - help them connect it to their existing information.
- As above, allow multiple OpenIDs to be connected to each User account
OpenID allows its Providers to return additional information beyond [name, email, etc] [2]. So different OpenID profiles might store different bonus information.
AOL might expose my AOL buddies list.
LiveJournal might expose my LiveJournal buddies.
A user could login to your app with both OpenIDs, connect it to one User account, and re-use all their buddies within your app.
Its awesome, and its the “Social OS” that everyone’s harping on about.
Its the future. And its not Facebook.
[1] [History lesson] Live Journal - now owned by A List ApartSix Apart - invented OpenID. [/History Lesson]
[2] Through a draft specification OpenID Attribute Exchange; very nifty indeed as the raw OpenID1.1 spec has very limited profile data sharing. Like none.
Where oh where are my rails plugins?
I can’t be the only person to notice the disappearance of old faithful plugins like simply_helpful and others.
They got deprecated. Enough of their code is now in rails edge that if you’re on rails edge you don’t ever need the plugins.
Great.
Um, except not on rails edge. That is, new apps using 1.2.3, or old apps where you’ve imported the plugin via piston and now piston is spitting the dummy that the original svn url is wrong.
Josh Peek Josh Knowles [via ticket] educated me to their new hide-y-hole in the plugin legacy list (svn list http://svn.rubyonrails.org/rails/plugins/legacy/).
They got legacified then, instead.
So, I guess (i.e. what I did was…) you need to svn remove the old plugin, and then re-install it. This time, don’t use piston or svn:externals as I doubt those plugins are ever going to change again.
ruby script/plugin install http://svn.rubyonrails.org/rails/plugins/legacy/simply_helpful
And life moves on again.
I Still Call Australia Home
On the 25th of September I’m doing a one-day workshop for Beginning Ruby on Rails (announcement) at the uber-cool Web Directions South [1].
Which is 2 days after I return back to Australia.
Which is 3 days after I leave RailsConfEurope.
And all this is over 2 years since I left Australia, thinking I might never come home.
Unknown natural forces [my wife] have conspired against me [I’m not in charge anyway] and its time to return to Brisbane, Australia .
Want to learn more about Brisbane? Here’s a graph…
It has 15% of its only dam remaining (the Wivenhoe).
And I’m confident its not the good 15%.
So, for Xmas, please send me bottles of drinking water. And beer.
Hack day in Sydney
So, since I’ll be in Sydney from the 25th to the 28th of September, and I’m free on the 26th, let me know below if you want to have a hack day. Sure, its a work day. But are all days work days? Also, I’m on irc #ror_au during Europe hours
[1]: If you read this blog, then the one-day workshop is not for you. But it might be for you friend, who lives in Sydney, Australia. That person should go. Tell them to. I’m awesome live and you’ll look really good.


