Dr Nic

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:

no-certificates

Or on Safari/KeyChain:

Keychain Access - no certificates

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

myopenid-create-certificate

After submit:

myopenid-creating-certificate-dialog

And then:

myopenid-certificate-creation-finished

Then feedback:

SSL Client Certificates

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:

myopenid-login-uses-alternate-url

myopenid-signin-with-certificate-form

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:

recent activity

Your sites and the future

As a web developer, you can do one of two things to get some leverage of Client Certificates.

  1. 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.
  2. 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

By: Captain Half/Byte And The Funky Maeshrooms
Create a unique beat loop that sounds just like you, then wear it anywhere on and off the web.

A soundbagde is a gravatar like widget, only for sounds instead of graphics. The sounds are individually rendered, based on a personal questionaire.

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Hypertr

By: Babaka
An app to connect different language speaking bloggers

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Linga

By: Army Of Monkeys
"Let your impressions 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…

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Joglog

By: Gdynia Belfast
The joglog is a simple, motivating running log. It provides good feedback on your training, allowing you to track dates, times, distances and many other parameters.

You may login as test:test to see real running data.

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Admiteer

By: Skype Smilies
In haiku:

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…

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Painboard

By: Self Grasping Robots
online photo editor and imageboard

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Geekhumour.Com

By: Sexy Migrations
A site to collect together all those funny snippets people come accross on the web, and integrate them in to email .sigs, and other places.

Your ratings are searchable so you can use your highest rated snippets automatically on the site or via rss

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Nourish

By: Team Integral
Nourish allows users to create and track well designed, RSS driven newsletters. Publishers can mash-up more than one feed, specify timed delivery, and customize the email templates to make simple RSS-driven newsletters.

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Fish4brains

By: Team O19s
Send your fish swimming through the waters of your favorite social network, or the internet at large! Don’t like what your fish finds? Tweak it’s genetic code!

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Researchr

By: Team Agador Sparticus
Writing tool that helps you write research papers. Lets you outline and structure your thoughts, then fill in the text. Keeps track of bibliographic information and helps to format everything properly at the end. Something like Scrivener, but hosted…

View Team Profile |
Try It Out! (Voting Starts Wednesday)

Ruby Brigade

By: Paint It Red
rubybrigade.org will be a geographically aware registry of ruby user groups across the world.

View Team Profile |
Try It Out! (Voting Starts Wednesday)

(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

OpenID 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=xml to XML output; JSON is default
  • add &meta=columns to include the column schema definitions
  • add &table_name=wp_posts for 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…

OpenId logo

In the mean-whilst…

Later that very same day, Dr Nic reads a tutorial by Joseph Smarr at PlaxoA 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.

User account with multiple OpenIDs

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 code
  • config/routes.rb – we need more routes to support alternate callbacks from OpenID providers
  • sessions_controller.rb and users_controller.rb – they are emptier and cleaner now
  • test\functionals\sessions_controller.rb – at the bottom are some test cases for the login/registration features of user_openids_helper

So, grab your Code Camera…