2015-02-10 101 views
0

我嘗試在我的網站ChercheAvocat(Rails 3)上安裝FB Connect,但點擊「使用Facebook登錄」鏈接後,出現錯誤「參數app_id是必需的」。 URL:https://www.facebook.com/dialog/oauth?client_id=&display=popup&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcustomers%2Fauth%2Ffacebook%2Fcallback&response_type=code&scope=email&state=fbe0db878d8d47b896aa18f40e00d5f1e117dde9bef55de9Facebook Connect - 參數app_id是必需的

我已經在Developers.Facebook上創建了應用程序(Chercheavocat-login),我已將該ID添加到代碼中。

你能幫我嗎?

的routes.rb

ChercheAvocat::Application.routes.draw do 

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

     root :to => 'home#index' 

初始化/ devise.rb

require "omniauth-facebook" 
Devise.setup do |config| 

    config.secret_key = 'xxxxxxxxx' 

    config.omniauth :facebook, 
     ENV['FACEBOOK_KEY'], 
     ENV['FACEBOOK_SECRET'], 
     :scope => 'email', 
     :display => 'popup', 
     :provider_ignores_state => true 

的意見/問題/ no_answers.html.erb

<script> 
    window.fbAsyncInit = function() { 
    FB.init({ 
     appId  : '1400942343542259', 
     xfbml  : true, 
     version : 'v2.2' 
    }); 
    }; 

    (function(d, s, id){ 
    var js, fjs = d.getElementsByTagName(s)[0]; 
    if (d.getElementById(id)) {return;} 
    js = d.createElement(s); js.id = id; 
    js.src = "//connect.facebook.net/en_US/sdk.js"; 
    fjs.parentNode.insertBefore(js, fjs); 
    }(document, 'script', 'facebook-jssdk')); 
</script> 

型號/ customer.rb

require 'digest/sha1' 

class Customer < ActiveRecord::Base 
    # Include default devise modules. Others available are: 
    # :confirmable, :lockable, :timeoutable and :omniauthable 

    # Setup accessible (or protected) attributes for your model 
    attr_accessible :email, :password, :password_confirmation, :remember_me 
    MIN_PASSWORD_LENGTH = 6 
    MAX_PASSWORD_LENGTH = 32 
    SALT = '")cvvvvvv("f!dsqf!!rffffqf/qdddf+/79dddd54' 

    devise :database_authenticatable, :registerable, 
    :recoverable, :rememberable, :confirmable, 
    :omniauthable, :omniauth_providers => [:facebook] 

    validates_presence_of :civility, :first_name, :last_name, :email 
    validates :email, :email => true 
    validates_uniqueness_of :email 
    validates_confirmation_of :email 
    validates_presence_of :password, :on => :create 
    validates_length_of :password, :within => MIN_PASSWORD_LENGTH..MAX_PASSWORD_LENGTH 
    validates_confirmation_of :password 
    validates_presence_of :zip_code, :city, :on => :update 

    acts_as_mappable 
    acts_as_url :id_full_name, :sync_url => true # uses attributes :url 

    attr_accessor :password 

    has_many :contact_requests, :dependent => :nullify 
    has_many :questions, :dependent => :nullify 
    has_many :subscriptions, :dependent => :destroy 

    before_validation(:encrypt_password, :on => :create) 
    before_save  :ensure_geolocation, :setup_modifications 
    after_create  :sync_subs 
    before_update  :check_sub_related_changes 
    after_update  :sync_sub_related_changes 

    CIVILITIES_ORDERED = ['mr', 'mrs', 'miss'] 

    scope :for_search, lambda { |params| 
    custom_scope = self.scoped 
    (params[:search] || '').split(/\s+/).each { |word| 
     custom_scope = custom_scope.where(
     'first_name like ? or last_name like ? or email like ?',"%#{word}%", "%#{word}%", "%#{word}%" 
    ) 
    } 
    custom_scope 
    } 

    def self.civilities(format = :short) 
    CIVILITIES_ORDERED.map { |code| [I18n.t(format, :scope => "civilities.#{code}"), code] } 
    end 

    [:short, :long].each do |code| 
    define_method "#{code}_civility" do 
     I18n.t(code, :scope => "civilities.#{civility}") 
    end 
    end 

    def full_name 
    "#{first_name.strip.titleize} #{last_name.strip.titleize}" 
    end 

    def id_full_name 
    "#{id}-#{full_name}".to_url 
    end 

    def to_param 
    url 
    end 

    def geolocated? 
    lat.present? && lng.present? 
    end 

    def greeting_name 
    "#{short_civility} #{last_name.strip.titleize}" 
    end 

    def has_sub?(kind) 
    subscriptions.kind(kind).count > 0 
    end 

    def obfuscation 
    [long_civility, last_name.present? && "#{last_name.strip[0, 1].mb_chars.upcase}." || nil].compact.join(' ') 
    end 

    # Set a new activation_code to persist password until email sent 
    def regenerate_password! 
    self.password = self.class.random_pronouncable_password 
    save(validate: false) 
    end 

    def result_name 
    "#{short_civility} #{last_name.titleize}" 
    end 

    # Designed to be used from customer forms (either back or authenticated front). 
    # kinds is a complete list of subscribed kinds. 
    # DEV NOTE: this impacts the DB immediately and may raise AR exceptions. 
    def subscription_kinds=(kinds) 
    if new_record? 
     @_required_kinds = kinds 
     return 
    end 
    Subscription.transaction do 
     self.subscriptions = subscriptions.select { |s| kinds.include?(s.kind.to_s) } 
     subscriptions.update_all :revocation => nil 
     subscriptions.update_all({:confirmation => Time.zone.now}, :confirmation => nil) 
     kinds_left = subscriptions.map { |s| s.kind.to_s } 
     (kinds - kinds_left).each do |k| 
     s = subscriptions.create! :email => email, :kind => k.to_sym 
     s.activate! 
     end 
    end 
    end 

    def self.authenticate(email, pass) 
    customer = find_by_email(email) 
    if customer 
     expected_hashed_pwd = generate_encrypted_password(customer.salt, pass) 
     customer = nil unless expected_hashed_pwd == customer.hashed_password 
    end 
    customer 
    end 

    def self.find_for_facebook_oauth(auth, signed_in_resource=nil) 

    customer = Customer.where(:provider => auth.provider, :uid => auth.uid).first 
    if customer 
     return customer 
    else 
     registered_customer = Customer.where(:email => auth.info.email).first 
     if registered_customer 
     return registered_customer 
     else 
     customer = Customer.create(name:auth.extra.raw_info.name, 
          provider:auth.provider, 
          uid:auth.uid, 
          email:auth.info.email, 
          password:Devise.friendly_token[0,20], 
         ) 
     end 
    end 
    end 

    private 
    def check_sub_related_changes 
    @email_changed = email_changed? 
    true 
    end 

    def encrypt_password 
    self.salt = object_id.to_s + rand.to_s 
    self.hashed_password = self.class.generate_encrypted_password(salt, password) 
    end 

    def ensure_geolocation 
    return true if geolocated? 
    base = [address1, address2, zip_code, city, 'France'].reject(&:blank?).map(&:strip).join(' ') 
    return true if base.blank? 
    geocoding = Geokit::Geocoders::MultiGeocoder.geocode(base) 
    auto_geocoded = geocoding.success? && geocoding.accuracy >= 4 # Town level 
    self.lat, self.lng = geocoding.lat, geocoding.lng if auto_geocoded 
    true 
    end 

    def self.generate_encrypted_password(salt, pass) 
    Digest::SHA1.hexdigest(salt.to_s + SALT.gsub(/\W/, '').downcase + pass.to_s) 
    end 

    def self.random_pronouncable_password(size = 4) 
    c = %w(b c d f g h j k l m n p qu r s t v w x z ch cr fr nd ng nk nt ph pr rd sh sl sp st th tr) 
    v = %w(a e i o u y) 
    (1..size * 2).inject('') { |acc, n| acc << (0 == n % 2 ? c[rand * c.size] : v[rand * v.size]) } 
    end 

    def setup_modifications 
    encrypt_password if password.present? 
    # self.password = nil # DEV NOTE: we need to keep it in memory for forgotten-password e-mail notifications 
    # Subscription.find_all_by_email(self.email).each { |s| s.customer = self } 
    end 

    def sync_subs 
    Subscription.update_all({ :customer_id => id }, :email => email) 
    self.subscription_kinds = @_required_kinds unless @_required_kinds.blank? 
    end 

    def sync_sub_related_changes 
    changes = {} 
    changes[:email] = email if @email_changed 
    subscriptions.update_all(changes) unless changes.empty? 
    end 

    def validate 
    if hashed_password.blank? 
     errors.add(:password, :blank) 
    end 
    if !email.blank? && !email_confirmation.blank? && email != email_confirmation 
     errors.add(:email, :confirmation) 
    end 
    errors.add(:terms_of_use, :accepted) unless terms_of_use 
    end 
end 

控制器/客戶/ omniauth_callbacks_controller.rb

class Customers::OmniauthCallbacksController < Devise::OmniauthCallbacksController 

    def facebook 
    @customer = Customer.find_for_facebook_oauth(request.env["omniauth.auth"], current_customer) 
    if @customer.persisted? 
     sign_in_and_redirect @customer, :event => :authentication #this will throw if @customer is not activated 
     set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format? 
    else 
     session["devise.facebook_data"] = request.env["omniauth.auth"] 
     redirect_to new_customer_registration_url 
    end 
    end 

end 
+0

現在請改變config.secret_key'的'生產值色器件初始化和'SALT'在客戶類... 他們應儲存在環境變量;) – 2015-02-10 11:47:43

+0

我做了改變 – 2015-02-10 12:54:26

+0

是否發生錯誤當加載Facebook頁面登錄時,或從它返回到您的網站? 請包含視圖的摘錄,以顯示您如何創建登錄鏈接! – 2015-02-12 10:03:22

回答

1

有在由OmniAuth產生與Facebook在URL中有app_id參數。

您是否正確配置了您的應用? 你應該在你的設計初始就OmniAuth特定行,是這樣的:

config.omniauth :facebook, "APP_ID", "APP_SECRET" 

APP_IDAPP_SECRET在您創建的Facebook應用的詳細信息頁面中找到。再次,保持它們的私密性。您不應將它們直接放入代碼中,而應使用Figaro gem和環境變量。

上OmniAuth Facebook的實施與設計的全部詳細資料有:https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview

更新
所以這實際上做,但你有沒有驗證這些環境變量真正建立正確的價值觀?
我看到你正在本地主機上運行你的服務器,然後假設開發環境;你有沒有將這些環境變量添加到你的環境中?

+0

是的,它在文件中,我只是在問題中添加它。 – 2015-02-12 10:32:52

+0

謝謝Olivier,我需要配置ENV數據。正如你告訴我的,我使用了費加羅寶石。 [https://github.com/laserlemon/figaro] – 2015-02-13 09:59:41