Thursday, June 28, 2012

Rails 3 : Devise : Omniauth : Facebook authentication

Today most of the clients prefer to have option for authentication through social sites as facebook/ twitter. As working of one of the project I was also adding facebook authentication for client project. Now the first choice was ofcourse to see if Ryan added any video for facebook authentication.

And I was lucky to get #360 Facebook Authentication just added, when I was looking for it. But I also found that there were some issues with devise, as I was not able to login.

I mean the simple project authentication was working great as shown. But when devise comes in it was having some problems. So I searched for devise and omniauth. And I found that I ignored the devise was ominauthable. So now I followed the step suggested in devise : omniauth :overview
Made some changes in my project as given in the example.

GemFile
gem "omniauth-facebook"
And run bundle command.
In devise.rb at top
require "omniauth-facebook"
and just uncommented
config.omniauth :facebook, "APP_ID", "APP_SECRET"
and changed the app_id and secret as per my facebook app generated.
I faced OpenSSL error but also the solution is available in the same example. Just changed the above config line to
config.omniauth :facebook, "APP_ID", "APP_SECRET",
      {:scope => 'email, offline_access', :client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}} 
Ofcourse app is running on heroku. Then added
:omniauthable
to the devise settings in user.rb (devise model)
In routes.rb I changed the previous single line
devise :users
to

devise_for :users, 
           :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }

added a migration file
class AddAuthFieldsToUser < ActiveRecord::Migration
  def change
    add_column :users, :provider, :string
    add_column :users, :uid, :string
    add_column :users, :oauth_token, :string
    add_column :users, :oauth_expires_at, :datetime
  end
end
run rake db:migrate to add the columns in users table and add same columns to attr_accessible in user.rb. After that created new folder in controllers as users where added omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def passthru
  render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
    # Or alternatively,
    # raise ActionController::RoutingError.new('Not Found')
  end

  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
      redirect_to stores_path, :event => :authentication, :current_user => @user
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end
In user.rb just removed name as its not in my users table.
  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.oauth_token = auth.credentials.token
      user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.save!
    end
  end
  
  def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth.provider, :uid => auth.uid).first
    unless user
      user = User.create(  provider:auth.provider,
                           uid:auth.uid,
                           email:auth.info.email,
                           password:Devise.friendly_token[0,20]
                           )
      user.ensure_authentication_token!
      # added extra to create authentication token for user
    end
    user
  end
The main steps are done. Just need to restart the server and way to go with facebook authentication for my project.
The given wiki is also contains the example for OpenID and Google. Check the link given at the start. Hope that will solve your problem, as it solved mine.

Sunday, June 24, 2012

Javascript : Debugging Object

Debugging JavaScript with firebug and other available tools is pretty easy. But without it this may seem little complex. But while checking out for solution I really found very helpful function at breakingpar.com.

The solution they provided was really great to know what the object is containing. function dumpProps uses confirm prompts for debugging inner objects or main objects with our requirement.

Once we found what we want we can just click cancel for coming out the loop or keep continuing debugging.