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

假装没事ソ 提交于 2019-12-29 11:43:43

问题


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 :) )


回答1:


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@example.com"
  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.




回答2:


Add this in gem file

gem 'omniauth-twitter'

do bundle and restart the server

after this add your app_id and secret key in config/initializer/devise.rb

require "omniauth-twitter"
config.omniauth :twitter, "app_id", "secret_key"

edit your user model

:omniauth_providers => [:facebook,:twitter]
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(name:auth.extra.raw_info.name,
                         provider:auth.provider,
                         uid:auth.uid,
                         email:auth.info.email,
                         password:Devise.friendly_token[0,20]                            
                         )

  end
  user
end

Add new controller file in

app/controller/user/omniauth_callbacks_controller.rb

 def twitter
@user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)

if @user.persisted?
  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
  session["devise.twitter_data"] = request.env["omniauth.auth"]
  redirect_to new_user_registration_url
end

end

Add this link in your view file

<%= link_to 'sign in with twitter', user_omniauth_authorize_path(:twitter) %>

Changes required in user model as twitter do not return user's email id: Create a migration file to allow null values in user's email column:

class ChangeEmailToNullInUser < ActiveRecord::Migration
  def up
      change_column :users, :email, :string, :null=>true
   end

  def down
  end
end

after this you also need to override user model validations so add this in user.rb

def email_required?
   false
end

Note: Before performing this you should create your app on twitter and give proper callback path. Its is important as after authentication from twitter the controller would come back to the path which you specify in your app on twitter.

let me know if you have any problem.




回答3:


Mattherick has a good solution for the email but I couldn't get the before_create to work. A callback doesn't play nice with conditional if statements because anything after the comma is meant to be an options hash. Therefore:

before_save :set_dummy_email, if self.provider == "twitter"

Popped me errors.

This how I fixed this:

before_save :set_dummy_email

def set_dummy_email
  self.email ||= "#{self.name}-CHANGEME@example.com"
end

This will only set an email if one is not given by the provider (ie: Twitter).

And then for a more 'universal' way of setting attributes (so you don't need a unique strategy:

def self.from_omniauth(auth)
  where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
    user.name = auth.info.name || auth.info.nickname
    user.provider = auth.provider
    user.uid = auth.uid
    user.email = auth.info.email if auth.info.email
    user.save
  end
end


来源:https://stackoverflow.com/questions/18504308/getting-omniauth-facebook-and-omniauth-twitter-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!