OpenIdAuthentication in detail
I’m currently working on my own little project to fill photolog.at with something interesting and for this I decided to use OpenID only. Dr. Nic is a big fan of OpenID and of course I hearted his advice of allowing one account with many identities
The first OpenID plugin I tried was OpenIdAuthentication which would also support regular loging and I can say: it’s just perfect for me. The reason why I decided to allow OpenID login only is that the application will closely integrate into flickr, which is via Yahoo, one of the many OpenID providers. This way I save the obviously unnecessary authentication system, and a bunch of fields on the User model.
How OpenID works
The idea is very simple, you come to my website, type in your OpenID url, my website will forward you to your OpenID provider and there you might have to login. The OpenID provider then sends you back to my website and if nothing went wrong my website will know a few basic details about you, create an account if not existing yet and you are logged in. It’s very easy and quick, no need to type in data that you already stored at your provider.
In my webapplication most of those steps are automatically done by the OpenIdAuthentication plugin, but if you are sophisticated then you might want to change a few things from the example in the README. For instance, it doesn’t tell you anything about creating a new user, or – Dr. Nic’s favourite – how to assign multiple identities to one user. I’ve added all of those functions to my version of the SessionsController. But first a few lines from the models.
Models
The User model is quite simple, only login, name and email. All three will be gathered from the OpenID provider but you might want to add a edit form.
To comply with the mulitiple identities requirement I’ve added an Identity model which contains the identity_url (this quite replaces password and login in a normal user model) and user_id. A user has_many :identites and each Identity object belongs_to :user (just for the case you still have to look this up…).
The OpenIdAuthentication Plugin adds two models, OpenIdAuthentication::Association and OpenIdAuthentication::Nounce but we don’t touch these.
The SessionsController
Best is to follow OpenIdAuthentication’s README and then replace the SessionsController. Please read me comments and let me know what you think about it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
class SessionsController < ApplicationController # sadly necessary skip_before_filter :verify_authenticity_token, :only => :create def create if using_open_id? # first difference: we explicitly pass the parameter. Much cleaner anyways than hiding it in the plugin... open_id_authentication(params[:openid_identifier] || params[:openid_url]) end end protected def open_id_authentication(identity_url) # also possible for :required and :optional are %w( dob gender postcode country language timezone ) # have a look here: http://openid.net/specs/openid-simple-registration-extension-1_0.html authenticate_with_open_id(identity_url, :required => [ :nickname, :email ], :optional => :fullname) do |result, identity_url, registration| if result.successful? # successful request, time to check wether the identity already exists identity = Identity.find_or_create_by_identity_url(identity_url) if identity.user.nil? # identity doesn't exist, but maybe the user? We use email as a unique key for this. if user = User.find_by_email(registration["email"]) logger.debug("using user '%s'" % user) # assign identity to an existing user identity.user = user identity.save else logger.debug("creating new user '%s'" % registration["nickname"]) # create a new user for the identity. identity.user = User.create!(:login => registration["nickname"], :name => registration["fullname"], :email => registration["email"]) identity.save end end # log the user in @current_user = identity.user successful_login else # oops. failed_login result.message end end end private def successful_login session[:user_id] = @current_user.id redirect_to(root_url) end def failed_login(message) flash[:error] = message redirect_to(new_session_url) end end |
Leave a Reply