Rails,Devise& Omniauth - 设置问题

时间:2015-11-03 07:14:00

标签: ruby-on-rails devise omniauth

我正在尝试(再次)使用Rails 4,devise和omniauth设置身份验证。

我尝试按照这篇文章中的示例进行操作:Rails 4, Devise, Omniauth (with multiple providers)

我安装了这些宝石:

gem 'devise'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-linkedin-oauth2'
gem 'oauth2'

我有一个用户模型和一个身份验证模型。

我有:

User.rb:

  has_many :authentications

def disapprove
    self.approved = false
  end

  def approve
    self.approved = true
  end

SOCIALS = {       facebook:'Facebook',       google_oauth2:'Google',       linkedin:'Linkedin'     }

def self.from_omniauth(auth, current_user)
  authentication = Authentication.where(:provider => auth.provider,
                                  :uid => auth.uid.to_s, 
                                  :token => auth.credentials.token, 
                                  :secret => auth.credentials.secret).first_or_initialize
  authentication.profile_page = auth.info.urls.first.last unless authentication.persisted?
  if authentication.user.blank?
    user = current_user.nil? ? User.where('email = ?', auth['info']['email']).first : current_user
    if user.blank?
      user = User.new
      user.skip_confirmation!
      user.password = Devise.friendly_token[0, 20]
      user.fetch_details(auth)
      user.save
    end
    authentication.user = user
    authentication.save
  end
  authentication.user 
end  

def fetch_details(auth)
  self.first_name = auth.info.first_name
  self.last_name = auth.info.last_name
  self.email = auth.info.email
  self.image = URI.parse(auth.info.image)
end

Authentication.rb

 belongs_to :user

的routes.rb

devise_for :users,
             :controllers => {
:registrations => "users/registrations",


             :omniauth_callbacks => 'users/omniauth_callbacks',
           }

用户/ registrations_controller

class Users::RegistrationsController < Devise::RegistrationsController 
  #before_filter :check_permissions , :only => [ :new, :create, :cancel ] 
  #skip_before_filter :require_no_authentication 

  def check_permissions
    authorize! :create, resource
  end

  def index
    if params[:approved] == "false"
      @users = User.find_all_by_approved(false)
    else
      @users = User.all
    end
  end

    def create
        @user = User.new(user_params) #(params[:user])

        respond_to do |format|
          if resource.save
            # Tell the UserMailer to send a welcome email after save
            # {@user.send_admin_mail
            # @user.send_user_welcome_mail}

            format.html { redirect_to(profile_path(@user.profile))}
            #, notice: 'We have received your registration. We will be in touch shortly.') }
            #format.json { render json: root_path, status: :created, location: @user }
          else
            #format.html { redirect_to(root_path, alert: 'Sorry! There was a problem with your registration. Please contact us to sort it out.') }
            format.html { render action: 'new' }
            format.json { render json: @user.errors, status: :unprocessable_entity }
          end
        end
      end

      private
      def user_params
          params.require(:user).permit(:first_name, :last_name, :email, :password )
        end



      # protected
      #   def after_sign_up_path_for(resource)
      #     'subscribers/new'
      #   end
end

User / Omniauth_callbacks controller

类Users :: OmniauthCallbacksController&lt;设计:: OmniauthCallbacksController

    # try again following https://stackoverflow.com/questions/21249749/rails-4-devise-omniauth-with-multiple-providers

  def all
    user = User.from_omniauth(env['omniauth.auth'], current_user)
    if user.persisted?
      sign_in user
      flash[:notice] = t('devise.omniauth_callbacks.success', :kind => User::SOCIALS[params[:action].to_sym])
      if user.sign_in_count == 1
        redirect_to profile_path(@user.profile)
      else
        redirect_to root_path
      end
    else
      session['devise.user_attributes'] = user.attributes
      redirect_to root_path
    end
  end

  User::SOCIALS.each do |k, _|
    alias_method k, :all
  end
end

设计/新注册视图说:

<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>

    <div class="row">
        <div class="col-md-3 col-md-offset-3">
            <div class="row">
                <div class="col-md-12">

                  <%- if devise_mapping.omniauthable? %>
                          <div class="facebookauth">
                            <%= link_to "Join with Facebook", user_omniauth_authorize_path(:facebook) %>
                          </div>
                  <% end -%>

                </div>
              </div>


          <div class="row">
            <div class="col-md-12">

              <%- if devise_mapping.omniauthable? %>
                  <div class="googleauth">
                    <%= link_to "Join with Google", user_omniauth_authorize_path(:google_oauth2) %>
                  </div>
              <% end -%>
            </div>
          </div>



      <div class="row">
              <div class="col-md-12">

                  <%- if devise_mapping.omniauthable? %>
                      <div class="linkedinauth">
                        <%= link_to "Join with LinkedIn", user_omniauth_authorize_path(:linkedin) %>
                      </div>
                  <% end -%>
              </div>
      </div>

          <div class="row">
            <div class="col-md-12">

              <%- if devise_mapping.omniauthable? %>
                  <div class="twitterauth">
                    <%= link_to "Join with Twitter", user_omniauth_authorize_path(:twitter) %>
                  </div>
              <% end -%>

            </div>
          </div>

        </div>

        <div class="col-md-5">
          <div class="emailform">
            <div class="form-inputs", style="margin-left: 7%">


                  <%= devise_error_messages! %>
                  <%= f.input :first_name,  :label_html => {:class => 'deviselabels'}, autofocus: true, required: false, :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
                  <%= f.input :last_name, :label_html => {:class => 'deviselabels'}, required: false, :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
                  <%= f.input :email, :label_html => {:class => 'deviselabels'}, required: false, autofocus: false, placeholder: "Please use your work or university address", :input_html => {:maxlength => 55, :size => 40, class: 'lineitemdevise'} %>
                  <%= f.input :password, :label_html => {:class => 'deviselabels'}, required: false, placeholder: "Minimum 8 characters", :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
                </div>

            <div class="form-actions">
          <%= f.button :submit, "Join by email", :class => "dcpb" %>
         </div>
          <% end %>
          </div>
      </div>

我有另一个名为profile.rb的模型。

profile belongs_to user

问题:

  1. 这些都不起作用。当我点击每个社交媒体登录链接时,该页面将跳转到通过电子邮件表单注册。
  2. heroku日志错误消息显示:

    (facebook) Authentication failure! invalid_credentials: OAuth2::Error, : 
    2015-11-03T07:05:48.237549+00:00 app[web.1]: {"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"HD3mnzmSTEw"}}
    
    1. 当我使用电子邮件和密码通过电子邮件表单完成注册时,会识别用户名(导航栏中显示Hello,但是当我进入rails控制台时,用户未列出。
    2. 此外,当我点击用户名时,我收到一条错误消息,指出该配置文件不存在。 heroku日志说:

      (Couldn't find Profile with 'id'=3)
      
      1. 社交媒体注册是否需要另外一步来创建新用户?
      2. 我的下一步尝试:

        我已经改变了以上所有内容,并按照Railscasts视频中的方法再次尝试。

        我现在使用用户模型和身份验证模型。

        在user.rb中,我有:

          has_many :authentications, :dependent => :delete_all
        
        def apply_omniauth(omniauth)
              authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
        
        end
        

        authentication.rb

        belongs_to :user
        
        def self.from_omniauth(auth)
            where(auth.slice(:provider, :uid)).first_or_create do | user |
                authentication.provider = auth.provider
                authentication.uid = auth.uid
                authentication.user.first_name = auth.first_name
                authentication.user.last_name = auth.last_name
                authentication.user.image = auth.info.image
        
            end
          end
        

        Authentications_controller:

        class AuthenticationsController < ApplicationController
          before_action :set_authentication, only: [:destroy]
        
          def index
            @authentications = current_user.authentications if current_user
          end
        
        
          def create
            omniauth = request.env["omniauth.auth"]
            authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
            if authentication
              sign_in_and_redirect_user(:user, authentication.user.profile)
        
            elsif current_user
              current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
              redirect_to user.profile_url
            else
              user = User.new
              user.omniauth(omniauth)
              if user.save!
                sign_in_and_redirect_user(:user, user.profile)
              else
                session[:omniauth] = omniauth.except('extra')
                redirect_to new_user_registration_url
              end
            end  
          end
        
          def destroy
            @authentication.destroy
            respond_to do |format|
              format.html { redirect_to authentications_url }
              format.json { head :no_content }
            end
          end
        
          private
            # Use callbacks to share common setup or constraints between actions.
            def set_authentication
              @authentication = current_user.authentications.find(params[:id])
            end
        end
        

        在routes.rb中,我有:

         devise_for :users,
                     :controllers => {
                        :registrations => "users/registrations",
                   }
         patch '/auth/:provider/callback' => 'authentications#create'
        

        Omniauth.rb

        要求'omniauth-facebook' 要求'omniauth-google-oauth2'

        OmniAuth.config.logger = Rails.logger

         Rails.application.config.middleware.use OmniAuth::Builder do
           provider :facebook, ENV['FACEBOOK_ID'], ENV['FACEBOOK_KEY'],
             :scope => 'public_profile', info_fields: 'id,first_name,last_name,link,email',
             :display => 'popup',
             :client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}
        

        然后当我尝试这个时,我收到了这个错误:

        (facebook) Authentication failure! invalid_credentials: OAuth2::Error, : 
        2015-11-05T06:4839+00:00 app[web.1]: {"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"CrvXN22Z"}}
        

        我发现错误消息的下一部分是奇怪的,因为它引用了我不再使用的回调控制器(整个事情已被注释掉,并且没有路由)。

        Authentication failure! invalid_credentials: OAuth2::Error, : 
        2015-11-05T08:24:16.010951+00:00 app[web.1]: Processing by Devise::OmniauthCallbacksController#failure as HTML
        2015-11-05T08:24:16.012648+00:00 app[web.1]: Redirected to http://www.dder.com/users/sign_in
        

        进一步尝试

        我一直在努力与omniauth建立设计超过1。5年。这是我最近的尝试(遵循sitepoint.com/rails-authentication-oauth-2-0-omniauth上的Sitepoint教程)。我之前尝试过使用本教程而没有取得任何成功,所以我做了一些调整,尝试将其调整到其他教程的某些方面。

        我现在有:

        user.rb

          has_many :authentications, :dependent => :delete_all
        
        def apply_omniauth(omniauth)
              authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
        
          end
        

        authentication.rb

        belongs_to :user
          def self.from_omniauth(auth)
            where(auth.slice(:provider, :uid)).first_or_create do | user |
                authentication.provider = auth.provider
                authentication.uid = auth.uid
                authentication.user.first_name = auth.first_name
                authentication.user.last_name = auth.last_name
                authentication.user.image = auth.info.image
        
            end
          end
        

        身份验证控制器

            class AuthenticationsController < Devise::OmniauthCallbacksController
              before_action :set_authentication, only: [:destroy]
        
              def index
                @authentications = current_user.authentications if current_user
              end
        
        
              def create
                omniauth = request.env["omniauth.auth"]
                authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
                if authentication
                  sign_in_and_redirect_user(:user, authentication.user.profile)
        
                elsif current_user
                  current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
                  redirect_to user.profile_url
                else
                  user = User.new
                  user.omniauth(omniauth)
                  if user.save!
                    sign_in_and_redirect_user(:user, user.profile)
                  else
                    session[:omniauth] = omniauth.except('extra')
                    redirect_to new_user_registration_url
                  end
                end  
              end
        
              def destroy
                @authentication.destroy
                respond_to do |format|
                  format.html { redirect_to authentications_url }
                  format.json { head :no_content }
                end
              end
        
              private
                # Use callbacks to share common setup or constraints between actions.
                def set_authentication
                  @authentication = current_user.authentications.find(params[:id])
                end
            end
        

        注册控制器

        class Users::RegistrationsController < Devise::RegistrationsController 
          #before_filter :check_permissions , :only => [ :new, :create, :cancel ] 
          #skip_before_filter :require_no_authentication 
          # before_action :configure_permitted_parameters, if: :devise_controller? # Suggestion from Sitepoint tutorial - not currently implemented because not sure about the difference between this and set params.
        
          def check_permissions
            authorize! :create, resource
          end
        
          def index
            if params[:approved] == "false"
              @users = User.find_all_by_approved(false)
            else
              @users = User.all
            end
          end
        
          def create
            super
            session[:omniauth] = nil unless @user.new_record?
          end
        
          # THIS IS A SUGGESTION FROM SITEPOINT TUTORIAL 
          # protected
        
          #   def configure_permitted_parameters
          #     devise_parameter_sanitizer.for(:sign_up) << [:first_name, :last_name]
          #   end
        
        
          private
            def user_params
                  params.require(:user).permit(:first_name, :last_name, :email, :password )
            end
        
            def build_resource(*args)
                super
                if session[:omniauth]
                @user.apply_omniauth(session[:omniauth])
                @user.valid?
                end
            end  
        
        end
        

        路由

        devise_for :users,
                     :controllers => {
                        :registrations => "users/registrations",
                        :omniauth_callbacks => "authentications"
                        # :omniauth_callbacks => 'users/omniauth_callbacks',
                   }
        
        get '/auth/:provider/callback' => 'authentications#create'
        

        我可以通过以下方式检查这些路线:

        rake routes | grep auth
                              user_omniauth_authorize GET|POST /users/auth/:provider(.:format)                                                                 authentications#passthru {:provider=>/facebook|linkedin|twitter|google_oauth2/}
                               user_omniauth_callback GET|POST /users/auth/:action/callback(.:format)                                                          authentications#:action
                                                      GET      /auth/:provider/callback(.:format)                                                              authentications#create
        

        视图中的新注册部分

        <%- if devise_mapping.omniauthable? %>
                          <div class="twitterauth">
                            <%= link_to "Join with Twitter", user_omniauth_authorize_path(:twitter) %>
                          </div>
                      <% end -%>
        

        我真的不确定这条路是从哪里来的。不知道为什么它被命名为。

        视图中的新会话部分

         <%- if devise_mapping.omniauthable? %>
                          <div class="twitterauth">
                            <%= link_to "Login with Twitter", user_omniauth_authorize_path(:twitter) %>
                          </div>
                      <% end -%>
        

        当前错误:

        AbstractController::ActionNotFound at /users/auth/twitter/callback
        
        The action 'twitter' could not be found for AuthenticationsController
        

2 个答案:

答案 0 :(得分:1)

Devise带有一个与omniuth集成的开箱即用解决方案。你可以查看这些网址: 1. https://www.digitalocean.com/community/tutorials/how-to-configure-devise-and-omniauth-for-your-rails-application 这个显示与DigitalOcean的集成,但可以扩展到其他人。 2. https://github.com/plataformatec/devise/wiki/OmniAuth%3A-Overview 这个来自设计维基

希望有所帮助

答案 1 :(得分:0)

有一个名为dom的gem专门用于管理具有多个提供者的Devise。它让事情变得简单!

另外,我认为你应该阅读这些文章。我相信你可以用他们解决所有问题:

  1. Devise OmniAuth: Overview
  2. OmniAuth Managing multiple provaders