Social Network Authentications with Omniauth and Authlogic

As I discussed my experience developing an application in Rails 3 using authlogic and omniauth in my last post, Here is the tutorial as promised.

Step 1:

First of all you need to setup a Rails 3 application using authlogic gem. If you’re feeling some difficulty, try these tutorials for help:

Authlogic railcasts: http://railscasts.com/episodes/160-authlogic
Authlogic with Rails 3 tutorial: http://www.dixis.com/?p=352

Step 2:

Add the following line to your gemfile:

gem 'omniauth'

and then install the bundle

bundle install

Step 3:

Now create a ruby file in config/initializers. I’ve named it as omniauth.rb. Put the following code in it:

Rails.application.config.middleware.use OmniAuth::Builder do
    provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET'
    provider :facebook, 'APP_ID', 'APP_SECRET'
    # Mention other providers here you want to allow user to sign in with
end

* You can get the keys by creating an application on twitter and facebook each.

Step 4:

A user can be authenticated e.g., for twitter by redirecting him to:

http://HOST:PORT/auth/twitter

After authentication, user will be redirected back to

http://HOST:PORT/auth/twitter/callback

We can handle this callback and redirect user to a controller#action where we can process his registration and/or session handling as well as can fetch some useful data. For this, add the following lines in your routes.rb file:

resources :authentications
match '/auth/:provider/callback' => 'authentications#create'

Let’s add a couple of links on our login page which allow user to sign in via facebook or twitter:

<a href="/auth/twitter">Sign in with Facebook</a>
<a href="/auth/facebook">Sign in with Twitter</a>

Step 5:

Until here, we’ve nicely setup the user part of authentication. Now we’ve to handle the callback from social websites after authentication. Create a scaffold authentication. Here are required code snippets:

Authetications Migration:

class CreateAuthentications < ActiveRecord::Migration
    def self.up
        create_table :authentications do |t|
            t.integer   :user_id
            t.string    :provider
            t.string    :uid
            t.timestamps
        end
    end

    def self.down
        drop_table :authentications
    end
end

Authentication Model:

class Authentication < ActiveRecord::Base
    belongs_to :user
    validates :user_id, :uid, :provider, :presence => true
    validates_uniqueness_of :uid, :scope => :provider
end

Step 6:

And lastly, the authentications controller will be contain following code:

class AuthenticationsController < ApplicationController
  def create
    omniauth = request.env['omniauth.auth']
    authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])

    if authentication
      # User is already registered with application
      flash[:info] = 'Signed in successfully.'
      sign_in_and_redirect(authentication.user)
    elsif current_user
      # User is signed in but has not already authenticated with this social network
      current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
      current_user.apply_omniauth(omniauth)
      current_user.save

      flash[:info] = 'Authentication successful.'
      redirect_to home_url
    else
      # User is new to this application
      user = User.new
      user.authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
      user.apply_omniauth(omniauth)

      if user.save
        flash[:info] = 'User created and signed in successfully.'
        sign_in_and_redirect(user)
      else
        session[:omniauth] = omniauth.except('extra')
        redirect_to signup_path
      end
    end
  end

  def destroy
    @authentication = current_user.authentications.find(params[:id])
    @authentication.destroy
    flash[:notice] = 'Successfully destroyed authentication.'
    redirect_to authentications_url
  end

  private
  def sign_in_and_redirect(user)
    unless current_user
      user_session = UserSession.new(User.find_by_single_access_token(user.single_access_token))
      user_session.save
    end
    redirect_to home_path
  end
end

Step 7:

Update your user model and add following lines in it:

def apply_omniauth(omniauth)
  self.email = omniauth['user_info']['email']

  # Update user info fetching from social network
  case omniauth['provider']
  when 'facebook'
    # fetch extra user info from facebook
  when 'twitter'
    # fetch extra user info from twitter
  end
end

And here your go. You application is all ready for authentication with facebook and twitter. And yes, you can also register/signin using conventional email and password technique.

Interesting Fact:

The application that we’ve coded above not only supports one-time user registration using social networks. It also supports multiple social accounts for a single user. Yes, you read that right.

Remember, we have separate authentications table. So a user can have more than 1 way of  entering in the site. How to do this? Well, it’s as simple as reading this line. Once the user is signed in, you can redirect him again to the paths we used initially for signing him in. That is, you can write these lines again in your user’s homepage

<a href="/auth/twitter">Sign in with Facebook</a>
<a href="/auth/facebook">Sign in with Twitter</a>

and he’ll have multiple social accounts associated with same user. Of course, you can hide the links of those networks user has already connected with his account.

How is this done?

Go though the code of the authentications controller again. We’ve a nice little condition there:

elsif current_user
  # User is signed in but has not already authenticated with this social network
  current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
  current_user.apply_omniauth(omniauth)
  current_user.save

In case user is signed in, another authentication will be added for him and that’s all. Try to logout and login with newly added network. Yes, it meant to be as easy as 1,2,3…

Share the post if you liked it and it helped you.

Rails, Authentications, Social Networks and Me

In the first application I developed in Ruby on Rails, I was asked to give user the option of logging in the site without registration using his/her social network account. Being new to the RoR, it was a bit difficult to play with bit complex plugins. But somehow I created two sample applications, one for twitter and other for facebook. The gems/plugins I used at that time were twitter-auth and facebooker respectively.

It was all coming good until I had to incorporate them in one application. It was pretty painful process and required quite a bit of customization. Afterward, the next requirement was to allow user to merge accounts. Believe me, it was something that took some effort of even the experienced resources I was working with. It needed overriding of few methods and we even made some changes in the plugins’ sources especially the facebooker.

Last week, I had to develop a quite similar website in Rails 3. I seems to be a horror when I received the requirement document as I’d never worked in Rails 3 before. But when I actually started development, it worked like a charm. I’m duly impressed by the power of Rails 3 and it’s ease of use. In a far lesser time, my application is all ready with basic and social authentications.

As I write, most website owners will want to allow access to their sites using facebook, twitter and other social networking sites to save user from giving all the personal data again and remembering another username/password combination. So, I thought to publish what I’ve done so far to help other newcomers in Rails working out a solution.

This time, I used following gems to build solution:

  • Authlogic
  • OmniAuth
  • fbgraph
  • twitter

Though, OmniAuth is recommended with device, I used it with authlogic and it worked fine. OmniAuth is very powerful gem which not only allows you to authenticate but you can do everything related to social networks, like post a link on facebook, with it.

I’ll soon be releasing a complete tutorial for beginners on authlogic and omniauth. Till then, you can build an application with Rails 3 using authlogic.

Authlogic railcasts: http://railscasts.com/episodes/160-authlogic
Authlogic with Rails 3 tutorial: http://www.dixis.com/?p=352

Or you can google a bit and find a tutorial on authlogic+rails 3 of your liking.