I'm quite new with Ruby on Rails, so please bear with me.
I want to user to be able to sign in through Facebook. I've followed the railscast #265 Devise and OmniAuth (revised). In the railscast the user is asked for a emailadres before he logs into Facebook. I want to user to be able to sign in through Facebook without the need of a emailadres beforehand.
What I use: The Devise and Omniauth-facebook gems
My expectation: The user clicks on the link, logs in through Facebook, Facebook returns email, name, uid, my app save that to the database and the user is created.
What I get: The error message ActiveRecord::StatementInvalid in OmniauthCallbacksController#facebook
SQLite3::ConstraintException: constraint failed: INSERT INTO "users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "name", "provider", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "uid", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
This is my user.rb model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
# Nodig als :validatable uit is
#validates_presence_of :email, :if => :email_required?
#validates_uniqueness_of :email, :allow_blank => true, :if => :email_changed?
#validates_format_of :email, :with => Devise.email_regexp, :allow_blank => true, :if => :email_changed?
#
#validates_presence_of :password, :if => :password_required?
#validates_confirmation_of :password, :if => :password_required?
#validates_length_of :password, :within => Devise.password_length, :allow_blank => true
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.email
user.name = auth.info.name
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"], without_protection: true) do |user|
user.attributes = params
user.valid?
end
else
super
end
end
def email_required?
provider == :facebook
end
def password_required?
super && provider.blank?
end
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
end
This is my migration to add OmniAuth to the users (users already has email)
class AddOmniauthToUsers < ActiveRecord::Migration
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
add_column :users, :name, :string
end
end
And this is my OmniAuth Callback controller:
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Loged in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :facebook, :all
end
Thanks in advance
Edit: Stack trace:
app/models/user.rb:23:in `from_omniauth'
app/controllers/omniauth_callbacks_controller.rb:3:in `all'
Edit: Server log
The weird thing is. When I print auth
I can see the name, email, provider and uid in the server log. However then this appears:
INSERT INTO "users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "name", "provider", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "uid", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [["created_at", Tue, 26 Feb 2013 17:20:23 UTC +00:00], ["current_sign_in_at", nil], ["current_sign_in_ip", nil], ["email", nil], ["encrypted_password", ""], ["last_sign_in_at", nil], ["last_sign_in_ip", nil], ["name", nil], ["provider", "facebook"], ["remember_created_at", nil], ["reset_password_sent_at", nil], ["reset_password_token", nil], ["sign_in_count", 0], ["uid", "XXXXX"], ["updated_at", Tue, 26 Feb 2013 17:20:23 UTC +00:00]]
SQLite3::ConstraintException: constraint failed: INSERT INTO "users" ("created_at", "current_sign_in_at", "current_sign_in_ip", "email", "encrypted_password", "last_sign_in_at", "last_sign_in_ip", "name", "provider", "remember_created_at", "reset_password_sent_at", "reset_password_token", "sign_in_count", "uid", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
The name, email is for instance nil
but the provider and the uid (now XXXXX) is filled in. I added name and email myself in the user model. Maybe here is a problem? Might this help?
Whenever I put
p auth.info.email
(for a numerous of things you need to add the.info
), the server logs shows the emailadress.I thing it has to do with the fact that I changed my User model:
The error is gone now. I added
user.save!
I changed toauth.info.email
and I replacedfirst_or_create
tofirst_or_initialize.tap
So when I look in the console (
User.all
), the email, name, uid and provider is in the database.Apparently everything works out now. Tanks Harm for your help!
p.s: The server log still gives the questions marks, but does save the data in the records.