Zero Sign On - 1 better or Infinitely better than Single Sign On?
This article has no code in it. There are no TODO steps. Nothing to install. Its a picture of the future.
There is no reason to bookmark this article and read it another day. Its nearly all pictures. You can read it now.
This “picture of the future” was actually added to our browsers in the 90s. Netscape and MSIE3.0 both had it.
“It” is Client Certificates, and to me to means “never logging on with username/password NOR OpenID ever again”. Zero Sign On. It must be better than the much-targetted Single Sign On.
Client Certificates
Firefox 2.0:

Or on Safari/KeyChain:

Small problem: no website I’ve ever used has ever offered them, so I never knew they existed. I didn’t know what they did, nor as a web developer that I could create them for users who’d never need to login again.
Clifford Heath showed me the light. On #roro irc channel, we were exploring how “Zero Sign On” might be implemented using ssh-keygen, and browser plugins etc. Clifford mentioned client certificates and then someone else mentioned that MyOpenID already supported them. [someone = Michael Kedzierski]
I already had an myopenid account, so raced over to explore the new world of certificates.
Under “Authentication Settings”:

After submit:

And then:

Then feedback:

So I thought to test out if “Zero Sign On” actually worked. Normally, after logging out you’d need to submit username/password or if a site supported OpenID (yes myopenid.com is an OpenID provider which is a bit circular but bear with me) you login by entering your OpenID url and pressing Enter. Either way, you’ve got work to do.
Instead, I clicked “Login” link on the home page, and was redirected immediately to:


So it was still using cookies so that it could log me in immediately next time without clicking “login”, but either way, there is no username/password nor any other “type something here” login form. Just a “Remember Me” checkbox.
Finally, myopenid.com shows a log of your sign-in attempts:

Your sites and the future
As a web developer, you can do one of two things to get some leverage of Client Certificates.
- Support OpenID as a login mechanism. Users with myopenid.com accounts (or other openid providers that support client certificates) will benefit from automatic login to their openid page and instant redirection to your site. You’ll also be able to help new users import their profile data to get them started quickly.
- Implement Client Certificates yourself. I would have liked to have had a crack at this before posting about Client Certificates and all their sweet loveliness, but I didn’t. My bad. Instead, I found a nice step-by-step (plus comments with updates) on implementing Client Certificates
If you have/do implement Client Certificates in Ruby/Rails world, you’ll get a 1000 Happy Points from me if you open source it/blog about it. Happy Points are redeemable for Happiness in all countries.
Why supporting multiple OpenIDs per User is useful for users…
Web apps/services go down for maintenance (expected or erroneously) all the time.
What happens when your “sign on to all apps everywhere” OpenID server goes down for an hour? You might not be able to login to any site.
Your users may not be able to log into YOUR app.
MyOpenID is going down on the 12th of September (see below) for an hour.
Allowing them to login with multiple OpenIDs might alleviate any difficulty users have using/buying your stuff.
Even if they have clicked “Allow Forever”, and it appears that they never revisit their OpenID provider again when logging into your site, they actually still do via the wonders of HTTP redirection, and those zippy quick InterTubes we take for granted.
Supporting multiple OpenIDs in your app isn’t much more effort than supporting one OpenID. You just need an extra table and a form for creating them.
The Plaxo article on multiple OpenIDs per user kind of makes it all appear a bit daunting, IMO.
But with the sweet Ruby OpenID gem and starting points like Ben Curtis sample app or my multiple-openid sample app, it might take only a 1/2 day or a day to educate yourself about OpenID, and hack it in to one of your apps.
Say, like your blog. Please.
This is a notice that MyOpenID will be having a maintenance
outage starting at 08:00 on 2007/09/12, Pacific Time.
The outage may last as long as 60 minutes, but is expected
to be considerably shorter.The reason for this outage is:
Database upgrade
During the outage, the MyOpenID website may be unavailable or
unresponsive, and users will be unable log into OpenID-enabled
websites using their MyOpenID accounts. The latest information about
this and other MyOpenID events can always be found on
http://janrain.com/blog/We apologize for the inconvenience. If you have any questions, please
contact us at support@myopenid.com.
RailsRumble hates OpenID
There are 146 RailsRumble entrants.
%w[rubygems hpricot open-uri].each { |l| require l }
(Hpricot(open(”http://vote.railsrumble.com”))/”div.app_summary”).size # => 146
Voting starts Wednesday. I’ve viewed a dozen or so front pages, and they all look awesome. But to use any a lot of them (I think) you need to create an account.
Unfortunately there are only 10 that used OpenID. (see pastie for code)
So for the other 136, you must now create usernames and accounts. Type your email and password 136 times.
UPDATE: RubyBrigade (list of Ruby clubs), Sake Bar (warehouse of Sake recipes), Irksome (irc log), Open Comic Book Database, PubBud, relocatr and Simplebucket do not require any authentication (added below)
Bonus Update: Jour de fĂȘte is a Facebook app. So, in a manner of speaking, it qualifies - Facebook signon is its OpenID equivalent.
For your convenience, here are links to the 10 sites that you can easily log into and play around with, via OpenID:
Soundbadge.Net
A soundbagde is a gravatar like widget, only for sounds instead of graphics. The sounds are individually rendered, based on a personal questionaire.
Hypertr
Linga
The marketing description (a.k.a. nonsense):
Linga is a hypertext meta-universe in which we can all cavort and play. Through the power of teh intertubes, and the the little truck-like links betwixt them, we provide c…
Joglog
You may login as test:test to see real running data.
Admiteer
hey, our band just got \
a gig at a local stage \
let’s sell some tickets \
+++++++++++++
In iambic pentameter:
This weekend we’ve been working ’round the clock \
to make this app functional and complete \
Now as our labor com…
Painboard
Geekhumour.Com
Your ratings are searchable so you can use your highest rated snippets automatically on the site or via rss
Nourish
Fish4brains
Researchr
Ruby Brigade
(todo - Jour de fĂȘte, Sake Bar (warehouse of Sake recipes), Irksome (irc log), Open Comic Book Database, PubBud, relocatr and Simplebucket)
Note: if your site is using OpenID but my script didn’t pick it, let me know below and I’ll add it.
Having suggested that OpenID is the answer to the 146 signups that are required for 146 applications, I actually think I’d be even cooler for RailsRumble (or any other competition) to have a single-signon mechanism, that once you signup, it tells all 146 apps to create you an account and log you in. THAT would be useful.
MagicCGI shows OpenID user count
In the last 20 days, 43 people have used OpenID to leave comments. That’s very cool.
Corollary: add OpenID login to your blog.
Even cooler - Dynamic counter
The screen shot comes from the Comments form.
If you look at the comments form in a week, month, year, the counter above have be changed from its original value 43.
No fancy Wordpress plugins (I don’t do PHP)
No Apache tricks. (I don’t know any Apache tricks)
Instead with Javascript/HTML attached to JSON attached to a RubyCGI script attached to my Wordpress database via ActiveRecords and some magic.
I call it MagicCGI. I also call it Frigging Scary.
Try the following:
$ curl -v "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_openid_identities&action=count&format=json" < Content-Type: txt/json < 43
Or some XML?
$ curl -v "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_openid_identities&action=count&format=xml" < Content-Type: txt/xml < <?xml version="1.0" encoding="UTF-8"?> <result> <count type="integer">43</count> </result>
Raw data?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_posts&field=post_title&limit=5&order=post_title"
[{"attributes": {"post_title": "OpenID count"}},
{"attributes": {"post_title": "MagicCGI shows OpenID user count"}},
{"attributes": {"post_title": "map_by_method now works with ActiveRecord associations"}},
{"attributes": {"post_title": "Feedburner"}},
{"attributes": {"post_title": "One year on the InterTubes"}}
]
Getting kinda scary now, I think.
Ooh, just how much magic?
Want a list of available tables to play with?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?meta=tables"
[{table_name: 'wp_users'},...]
- add
&format=xmlto XML output; JSON is default - add
&meta=columnsto include the column schema definitions - add
&table_name=wp_postsfor each table you want (instead of all the tables)
E.g. to see the columns for wp_posts and no other table, in XML:
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?meta=tables&meta=columns&table_name=wp_posts&format=xml"
A list of all urls and internal user_ids for users/OpenID users/registered commenters?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_users&field=user_url&field=id"
[{"attributes": {"id": "1", "user_url": "http://drnicwilliams.com"}}, ...
So, now we know Dr Nic = user id 1.
Oh oh oh, how about a list of comments for a specific user?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&user_id=1" ...comments by Dr Nic... or $ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&user_id=1&action=count" 232
Dr Nic’s commented in his own blog 232 times? Out of how many comments?
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&action=count" 963
Where are the user emails? Where are their passwords?
Hidden! See below.
Callbacks?
JSON URLs support callback=someCallbackMethod and/or variable=someLocalVariable.
$ curl "http://drnicwilliams.com/cgi-bin/wp_drnicwilliams.cgi?table=wp_comments&action=count&callback=someMethod" someMethod(963);
What does wp_drnicwilliams.cgi look like?
Something a little like this…
#!/usr/local/bin/ruby
require 'magic_cgi' # loads the render magic, model magic, and meta-model magic
include Render
require 'magic_cgi/config/wordpress' # connect to DB using Wordpress installation (wp-config.php)
MagicCGI::Config::Wordpress.establish_connection "/path/to/drnicwilliams/web/public"
# The following is defaulted for Wordpress connections:
MagicCGI::Config.hidden_tables |= %w[wp_openid_nonces wp_openid_associations wp_usermeta wp_tla_rss_map wp_tla_data]
MagicCGI::Config.hidden_columns['wp_users'] = %w[user_email user_pass user_activation_key]
MagicCGI::Config.hidden_columns['wp_openid_identities'] = %w[hash]
MagicCGI::Config.hidden_columns['wp_comments'] = %w[comment_author_email comment_author_IP]
MagicCGI::Config.hidden_columns['wp_posts'] = %w[post_password]
render do |params|
data = DbTable.from_params(params)
data ||= begin
table_name = params['table'].first || ‘wp_users’
model_name = ActiveRecord::Base.class_name(table_name)
klass = MagicCGI::MagicModel.create_class(model_name, ActiveRecord::Base)
klass.find_or_count_by_params(params)
end
data
end
Can I write my own CGI scripts for my own DBs?
Sure. Its a library called MagicCGI.
What is MagicCGI?
Coming soon.
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…



