Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting (omniauth-facebook) and (omniauth-twitter) work

I'm using:

  • Ruby on Rails 4
  • devise 3.0.3
  • omniauth (1.1.4)
  • omniauth-facebook (1.4.1)
  • omniauth-twitter (1.0.0)

I recently set up my omniauth-facebook and everything works fine. Now i want to add omniauth-twitter but somehow i mess things up, pretty bad.

1.) To set up my Omniauth-Facebook i did this (in a nutshell):

gem 'omniauth'
gem 'omniauth-facebook'

2.) Added the columns "provider" and "uid" to my User model.

3.) Next, i declared the provider in my config/initializers/devise.rb:

require "omniauth-facebook"
config.omniauth :facebook, "App_ID", "App_Secret",
                                {:scope => 'email,offline_access',
                                 :client_options => {:ssl => {:ca_file => 'lib/assets/cacert.pem'}},
                                 :strategy_class => OmniAuth::Strategies::Facebook}

4.) I edited my Model User.rb

# Facebook Settings
def self.find_for_facebook_oauth(auth, signed_in_resource = nil)
    user = User.where(provider: auth.provider, uid: auth.uid).first
    if user.present?
        user
    else
        user = User.create(first_name:auth.extra.raw_info.first_name,
                                             last_name:auth.extra.raw_info.last_name,
                                             facebook_link:auth.extra.raw_info.link,
                                             user_name:auth.extra.raw_info.name,
                                             provider:auth.provider,
                                             uid:auth.uid,
                                             email:auth.info.email,
                                             password:Devise.friendly_token[0,20])
    end
end

and added the attributes to devise:

:omniauth_providers => [:facebook]

5.) I edited the routes:

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

THE END

Although this worked perfectly for Facebook, i tried for hours now to get this working for Twitter, and i just cant figure it out.

If someone, who has experience in this, or just knows the solution could help me set this up, i would be very thankful :)

Thank you guys, and sorry for the long Post.

In Addition

Twitter does not provide an :email Attribute so i have to Split up my User Registration Process i guess ?

My Twitter action in my User Model

# Twitter Settings
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth[:provider], :uid => auth[:uid]).first
    unless user
        user = User.create(:first_name => auth[:name],
                                             :user_name => auth[:screen_name],
                                             :provider => auth[:provider], :uid => auth[:uid],
                                             :password => Devise.friendly_token[0,20]
        )
    end
    user
end

# build auth cookie hash for twitter
def self.build_twitter_auth_cookie_hash data
    {
        :provider => data.provider, :uid => data.uid.to_i,
        :access_token => data.credentials.token, :access_secret => data.credentials.secret,
        :first_name => data.name, :user_name => data.screen_name,

    }
end

I had to migrate a confirmable for Users -> How To: Add :confirmable to Users

My Form's Problem, (At Least im getting to this poing now :) )

enter image description here

like image 481
Mini John Avatar asked Aug 29 '13 07:08

Mini John


1 Answers

To fix your problem with the email you could just set a dummy mail, or add a second step where the user adds his/her email.

Dummy mail:

class User < ActiveRecord::Base

  before_create :set_dummy_mail, if self.provider == "twitter"

  private

  def set_dummy_mail
    self.email = "#{self.name}[email protected]"
  end

end

Or the second step option:

Modify your controller to redirect to an add email step if the provider is twitter and the email is blank. Maybe you also have to modify your validations to allow email blank on create if the provider is twitter.

UPDATE: I did it like following:

Gemfile:

gem "devise"
gem "omniauth"
gem "omniauth-facebook"
gem "omniauth-twitter"

I used:

  • devise version 2.2.3
  • omniauth 1.1.4
  • omniauth-facebook 1.3.0
  • omniauth-twitter 0.0.17

If you are using different versions, you maybe must change a few things..

routes.rb:

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

devise_scope :user do
  post "account/create" => "users/accounts#create"
end

app/models/user.rb

class User < ActiveRecord::Base

  # allow email blank for first create
  validates_format_of :email, :with => Devise.email_regexp, :allow_blank => true, :if => :email_changed?

  # facebook find method
  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(:first_name => auth.extra.raw_info.first_name, 
                         :last_name => auth.extra.raw_info.last_name, 
                         :facebook_link => auth.extra.raw_info.link, 
                         :user_name => auth.extra.raw_info.name
                         :provider => auth.provider, 
                         :uid => auth.uid, :email => auth.info.email, 
                         :password => Devise.friendly_token[0,20]
                        )
      user.confirm!
    end
    user
  end

  # twitter find method
  def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
    user = User.where(:provider => auth[:provider], :uid => auth[:uid]).first
    unless user
      user = User.create(:first_name => auth[:first_name], :user_name => auth[:user_name],
                         :provider => auth[:provider], :uid => auth[:uid], 
                         :password => Devise.friendly_token[0,20]
                        )
    end
    user
  end

  # build auth cookie hash for twitter
  def self.build_twitter_auth_cookie_hash data
    {
      :provider => data.provider, :uid => data.uid.to_i,
      :access_token => data.credentials.token, :access_secret => data.credentials.secret,
      :first_name => data.screen_name, :user_name => data.name,

    }
  end

end

app/controllers/users/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  # callback action for facebook auth
  def facebook
    @user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      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

  # callback action for twitter auth
  def twitter
    data = session["devise.omniauth_data"] = User.build_twitter_auth_cookie_hash(request.env["omniauth.auth"])

    user = User.find_for_twitter_oauth(data)
    if user.confirmed? # already registered, login automatically
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
      sign_in_and_redirect user, :event => :authentication
    elsif !user.email?
      flash[:error] = "You must add an email to complete your registration."
      @user = user
      render :add_email
    else
      flash[:notice] = "Please confirm your email first to continue."
      redirect_to new_user_confirmation_path
    end
  end

end

app/views/users/omniauth_callbacks/add_email.html.erb

<%= form_for(@user, :as => "user", :url => account_create_path, :html => {:class => "form-inline"}) do |f| %>
  <%= f.email_field :email, :placeholder => User.human_attribute_name(:email), :class => "input-medium" %>
  <%= f.submit "Finish registration", :class => "btn btn-small" %>
<% end %>

app/controllers/users/accounts_controller.rb

class Users::AccountsController < ApplicationController

  def create
    data = session["devise.omniauth_data"]
    data[:email] = params[:user][:email]
    user = User.find_for_twitter_oauth(data)
    user.email = data[:email]

    if user.save
      flash[:notice] = I18n.t "devise.registrations.signed_up_but_unconfirmed"
      redirect_to root_path
    else
      flash[:error] = I18n.t "devise.omniauth_callbacks.failure", :kind => data[:provider].titleize, :reason => user.errors.full_messages.first
      render "users/omniauth_callbacks/add_email"
    end
  end

end

Maybe you have to modify the one or other part of my solution..you also could refactor the two methods in the user model (find_for_facebook_auth, find_for_twitter_auth) to work with one dynamic method. Try it out and let me know, if you still have problems. If you find any typo, please also let me know.. Also you should write tests to check everything within your system.

like image 116
Matthias Avatar answered Nov 12 '22 21:11

Matthias