I try to install FB Connect on my website ChercheAvocat (Rails 3), but after clicking on the link "Sign in with Facebook" I have an error " The parameter app_id is required". 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
I have created the app (Chercheavocat-login) on Developers.Facebook and I have added the ID into the code.
Can you help me ?
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
There's no app_id
parameter in the URL that is generated by OmniAuth to sign in with Facebook.
Have you configured your app correctly? You should have a specific line in your Devise initializer concerning OmniAuth, something like:
config.omniauth :facebook, "APP_ID", "APP_SECRET"
The APP_ID
and APP_SECRET
are found in the details page of the Facebook App you created. Once again, keep them private. You should not put them directly in your code but rather use the Figaro gem and environment variables.
Full details on the implementation of OmniAuth Facebook with Devise are there: https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
Update
So this was in fact done, but have you verified these environment variables are really set to the correct values?
I see you're running your server on localhost, presuming a dev environment then; have you added those environment variables to your, well, environment?