Facebook 连接 - 参数 app_id 是必需的

Facebook Connect - The parameter app_id is required

我尝试在我的网站 ChercheAvocat (Rails 3) 上安装 FB Connect,但是在点击 link "Sign in with 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=fbe0db878d8d47b896aa18f40e00d5f1e117dde9bef55de9

我在 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'

initializers/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

views/questions/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>

models/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

controllers/customers/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

OmniAuth 生成的用于使用 Facebook 登录的 URL 中没有 app_id 参数。

您的应用配置是否正确? 你应该在你的 Devise 初始值设定项中有一个关于 OmniAuth 的特定行,比如:

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

APP_IDAPP_SECRET 可以在您创建的 Facebook 应用程序的详细信息页面中找到。再次强调,将它们保密。您不应将它们直接放入代码中,而应使用 Figaro gem 和环境变量。

有关使用 Devise 实施 OmniAuth Facebook 的完整详细信息,请参见:https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview

更新
所以这实际上已经完成了,但是您是否验证了这些环境变量是否真的设置为正确的值?
我看到你 运行 你的服务器在本地主机上,假设是开发环境;您是否已将这些环境变量添加到您的环境中?