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

后端 未结 3 745
无人共我
无人共我 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: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.

提交回复
热议问题