



We're going to learn the bare minimum required to allow your users to use their Google Account to register and sign in to your Ruby on Rails 3 web app. I think you'll find that it's very easy to add OpenID support for Google Accounts, especially for Rails 3 web apps.





Assumptions:

You are building a Ruby on Rails 3 web app.

You do not yet have user authentication or registration for your app. A future article will show you how to add Google OpenID to your authlogic app, but this article is all about starting from scratch.

You want your users to use their Google Accounts for their identity.

You have a model named User which represents a registered user in your system.



Step Zero: Add attributes to User

Although we are assuming you already have a User model, we need to add a identifier_url attribute. This attribute, which should be unique, stores the OpenID URL identifying the user.



You can use a migration like:





class AddIdentifierUrlToUsers < ActiveRecord::Migration def self.up add_column :users, :identifier_url, :string add_index :users, :identifier_url, :unique => true end def self.down remove_index :users, :identifier_url remove_column :users, :identifier_url end end



This code also assumes you have email , first_name , and last_name as attributes for your user, in addition to identifier_url . (Once you have Google Account authentication via OpenID, you are more than ready to publish your web app in the Chrome Web Store . I've written a quick start guide for the Chrome Web Store which will help you get your app into the store in under 30 minutes.)Although we are assuming you already have a User model, we need to add aattribute. This attribute, which should be unique, stores the OpenID URL identifying the user.You can use a migration like:This code also assumes you have, andas attributes for your user, in addition to





Step One: Add the gems

You'll need both ruby-openid and rack-openid added to your Gemfile. It's as simple as:





gem "ruby-openid" gem "rack-openid"





bundle install which will pull down the gems and lock them into your project.



Step Two: Tell Rails about Rack::OpenID



Open up config/application.rb and add





require 'rack/openid'



to the file of the file. Then, inside the Application you'll need to add





config.middleware.use 'Rack::OpenID' And then, of course, runwhich will pull down the gems and lock them into your project.Open upand addto the file of the file. Then, inside theyou'll need to add





Step Three: Create AuthenticationHelper

Create an authentication_helper.rb in the app/helpers directory. This file should include:





module AuthenticationHelper def signed_in? !session[:user_id].nil? end def current_user @current_user ||= User.find(session[:user_id]) end def ensure_signed_in unless signed_in? session[:redirect_to] = request.request_uri redirect_to(new_session_path) end end end





Step Four: Tell your app about AuthenticationHelper

Open up app/controllers/application_controller.rb and add the line include AuthenticationHelper





class ApplicationController < ActionController::Base protect_from_forgery include AuthenticationHelper end





Step Five: Add the routes

Open up config/routes.rb and add the line:





resource :session





Step Six: Create the SessionsController

Create app/controller/sessions_controller.rb which should look like:





class SessionsController < ApplicationController skip_before_filter :verify_authenticity_token def new response.headers['WWW-Authenticate'] = Rack::OpenID.build_header( :identifier => "https://www.google.com/accounts/o8/id", :required => ["http://axschema.org/contact/email", "http://axschema.org/namePerson/first", "http://axschema.org/namePerson/last"], :return_to => session_url, :method => 'POST') head 401 end def create if openid = request.env[Rack::OpenID::RESPONSE] case openid.status when :success ax = OpenID::AX::FetchResponse.from_success_response(openid) user = User.where(:identifier_url => openid.display_identifier).first user ||= User.create!(:identifier_url => openid.display_identifier, :email => ax.get_single('http://axschema.org/contact/email'), :first_name => ax.get_single('http://axschema.org/namePerson/first'), :last_name => ax.get_single('http://axschema.org/namePerson/last')) session[:user_id] = user.id if user.first_name.blank? redirect_to(user_additional_info_path(user)) else redirect_to(session[:redirect_to] || root_path) end when :failure render :action => 'problem' end else redirect_to new_session_path end end def destroy session[:user_id] = nil redirect_to root_path end end





Note that the create method handles both sign in and registration.





It's also very important to point out that even though this code asks for attributes like firstName and lastName, the OpenID provider might not return them to your app. So, you'll probably want to add a second step for new users to collect more information. This is accomplished with the user_additional_info_path(user) .





Step Seven: Create an error page

Just in case it didn't work, you can display a helpful message for your user. Create a app/views/sessions/problem.html.erb





<h1>DOH!</h1>

<p>Looks like your Google login didn't quite work. <%= link_to 'Try again?', new_session_path %></p>





Step Eight: Protect your pages

To ensure that only signed in users can access your resources, simply place before_filter :ensure_signed_in in any controller you wish to protect.





For example:





class AdditionalInfosController < ApplicationController before_filter :ensure_signed_in def show @user = User.find(params[:user_id]) end def update @user = User.find(params[:user_id]) @user.update_attributes(params[:user]) redirect_to(session[:redirect_to] || root_path) end end





Summary

Congrats, you've gone from zero user registration and authentication to a full OpenID based Google Account based system. All of this code can be found in the Bracket Baby app on Github

We're busy developers, you and I, and we want to get back to building our awesome HTML5 app and not muck around too much with user sign in and registration. There are enough accounts out there, there's no reason why your users need to create a new account identity just for your system. Let them sign in with an existing account, your users will thank you!!