Question

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?

Était-ce utile?

La solution

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:

  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.name = auth.info.name
      user.save!
    end
  end

The error is gone now. I added user.save! I changed to auth.info.email and I replaced first_or_create to first_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.

Autres conseils

I would guess you either try to create a User without all it's required field OR you are trying to create a User which already exists (I suspect there is a uniqueness contraint on email).

To eliminate the later; remove all users from the users table.

To test the former; try creating a user with User.create(some_params) with the same params as you are giving to the from_omniauth method. To figure these out use something like pry or simply do:

def self.from_omniauth(auth)
    p auth # Add this line and watch the logs.
    ....
end

What also might help is to look at your db/schema.rb (or db/structure.sql after running rake db:structure:dump) and see if there are any NOT NULL or UNIQUE constrains.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top