Monday, 13 October 2014

Textual description of firstImageUrl

Social Media Authentication On Rails- Part 2

In the last post , we have discussed how to get api keys and access for multiple social media platform .In this post we will to setup rails to integrate these applications .

Code SetUp:

Now its Time to Set Up Rails Application with MongoDB , Rails & Omniauth Gem(s).
we will be using “ruby 2.1.1p76” with “Rails 4.1.0” for our application.

Step 1:
In Gem file add the following Gems:
gem 'rails', '4.1.0'
gem 'mongoid', git: 'https://github.com/mongoid/mongoid.git'
gem 'devise'
gem 'bson_ext'
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-linkedin' 
gem 'omniauth-twitter' 
gem "omniauth-google-oauth2"
gem 'twitter'
gem "linkedin"
gem 'oauth2'
Step 2:
Follow the link and install devise gem to your application.
For Omniauth with devise follow the link which will be usefull for development and debugging your application.
Step 3:
Include required Gems in your initializers/devise.rb file and add your API KEYS with API Secret.
require 'devise/orm/mongoid'
require 'omniauth-twitter'
require 'omniauth-google-oauth2'
require 'omniauth-linkedin'
require 'omniauth-facebook'
require 'omniauth-github'

Configuring API Keys:
config.omniauth :twitter, "API KEY", "API SECRET"
config.omniauth :linkedin, "API KEY", "API SECRET", :scope => 'r_basicprofile r_emailaddress rw_nus'
config.omniauth :facebook, 'API KEY', 'API SECRET'
config.omniauth :google_oauth2, 'API KEY', 'API SECRET'

Add the following links to devise registrations/new or shared/_links.html.erb









Open Up Your User Model and Configure it as described below , fields for mongo db can be configured as per your requirement :
class User
  include Mongoid::Document
  devise :omniauthable, :omniauth_providers => [:facebook,:twitter,:linkedin,:google_oauth2]
  embeds_one :user_linkedin_connection, :class_name => 'User::LinkedinConnection'
  embeds_one :user_twitter_connection, :class_name => 'User::TwitterConnection'
  # Configured for Getting mongo key from Session in rails 4 (Mongoid)
  class << self
    def serialize_from_session(key, salt)
      record = to_adapter.get(key.first["$oid"]) if key.present? # to_adapter.get(key.to_s)
      record if record && record.authenticatable_salt == salt
    end
  end
  def self.connect_to_linkedin(auth)
    self.provider = auth.provider
    self.uid = auth.uid
    self.user_linkedin_connection = User::LinkedinConnection.new(:token => auth["extra"]["access_token"].token, :secret => auth["extra"]["access_token"].secret)
    unless self.save
      return false
    end
    true
  end
  def self.disconnect_from_linkedin!
    self.provider = nil
    self.uid = nil
    self.user_linkedin_connection = nil
    self.save!
  end
  def self.find_for_linkedin_oauth(auth, signed_in_resource=nil)
    @user = User.where(:provider => auth.provider, :uid => auth.uid).first
    if @user
      @user.connect_to_linkedin(request.env["omniauth.auth"])
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "LinkedIn") if is_navigational_format?
    else
      flash[:notice] = "Couldn't find a user connected to your LinkedIn account. Please sign in and then connect your account to LinkedIn."
      redirect_to new_user_session_url
    end
  end
  def self.find_for_facebook_oauth(auth)
   where(auth.slice(:provider, :uid)).first_or_create do |user|
     user.provider = auth.provider
     user.uid = auth.uid
     user.email = auth.info.email
     user.password = Devise.friendly_token[0,20]
     user.name = auth.info.name   # assuming the user model has a name
   end
  end
  def self.find_for_google_oauth2(access_token, signed_in_resource=nil)
    data = access_token.info
    user = User.where(:email => data["email"],:provider => "Google").first
    unless user
      user = User.create( name: data["name"],email: data["email"],provider: "Google", password: Devise.friendly_token[0,20]  )
    end
    user
  end
  def self.find_for_github_oauth(auth)
    record = where(provider: auth.provider, uid: auth.uid.to_s).first
    record || create(provider: auth.provider, uid: auth.uid, email: auth.info.email, password: Devise.friendly_token[0,20], name: auth.info.name )
  end
end
Create two More files named linkedin_connection.rb and twitter_connection.rb inside app/models/user/ which will contain the token and secret provided from provider.

linkedin_connection.rb:
class User::LinkedinConnection
  include Mongoid::Document
  include Mongoid::Timestamps
  embedded_in :user
  field :token
  field :secret
end 
twitter_connection.rb:
class User::TwitterConnection
  include Mongoid::Document           field :token
  include Mongoid::Timestamps         field :secret
  embedded_in :user
   end
Once we get a Success request from Provider , response moves on to omniauth call_back controller where we need to store the response and maintain the session.

Create a omniauth_callback controller and paste the code as provided below:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  before_filter :authenticate_user!
  # Linkedin authentication
  def linkedin
    @user = User.where(:provider => request.env["omniauth.auth"].provider, :uid => request.env["omniauth.auth"].uid).first
    if @user.present?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "LinkedIn") if is_navigational_format?
    else
      auth = request.env["omniauth.auth"]
      @user = User.new #create new user to save in the database
      @user.email=  auth.info.email #save user email
      @user.provider = auth.provider
      @user.uid = auth.uid
      @user.user_linkedin_connection = User::LinkedinConnection.new(:token => auth["extra"]["access_token"].token, :secret => auth["extra"]["access_token"].secret)
      @user.save(:validate => false) #password for the linkdin to be stored
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "LinkedIn") if is_navigational_format?
    end
  end

# Facebook authentication
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"])
    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

# Twitter authentication
def twitter
    @user = User.where(:provider => request.env["omniauth.auth"].provider, :uid => request.env["omniauth.auth"].uid).first
    if @user.present?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
    else
      auth = request.env["omniauth.auth"] # contains all the information about the user login profile.
      @user = User.create(name: auth.extra.raw_info.name,
                  provider: auth.provider,
                  uid: auth.uid,
                  email: auth.uid+"@twitter.com", #create user email
                  password: Devise.friendly_token[0,20],
                  user_twitter_connection: User::TwitterConnection.new(:token => auth.credentials.token, :secret => auth.credentials.secret )
      )
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "Twitter") if is_navigational_format?
    end
  end

 # Google authentication
  def google_oauth2
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.find_for_google_oauth2(request.env["omniauth.auth"])
    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, :kind => "Google") if is_navigational_format?
      # flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
      # sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
    else
      session["devise.google_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end
Now we are Good to Go ahead and start authenticating with social websites in our Rails app.

Note:
• Application status in Provider Settings should be live
• Call back URL should match your application routes
• It is Good to go with Storing session in DB as sometimes cookie_store fails when a logged in user fetches request from provider.


Thanks to Santosh for wroting this post .