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

后端 未结 3 726
无人共我
无人共我 2020-12-23 15:32

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)

相关标签:
3条回答
  • 2020-12-23 16:03

    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
    
    0 讨论(0)
  • 2020-12-23 16:20

    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.

    0 讨论(0)
  • 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.

    0 讨论(0)
提交回复
热议问题