I am using facebook-omniauth to authenticate user with Facebook. Everything works fine, except how can I build user profile table from extra fields that I receive from omniauth.
The relationship user to profile is has_one, and belongs_to as vice versa.
Following were my OmniauthCallbacksController
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user 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_user_registration_url
end
end
def failure
redirect_to root_path
end
end
In my user.rb model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook]
has_many :products
has_one :profile
has_one :store_setting
after_create :send_welcome_message
enum status: {
normal: 0,
merchant: 1
}
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
logger.info "status=from_omniauth first_name=#{auth.info.first_name} first_last_name=#{auth.info.last_name}"
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.build_profile(auth)
#user.name = auth.info.name # assuming the user model has a name
#user.image = auth.info.image # assuming the user model has an image
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
def build_profile(auth)
self.Profile.new(first_name:auth.info.first_name) --> not work!!
end
private
def send_welcome_message
UserMailer.welcome_message(self).deliver
end
end
Thanks!!
It seems that you are trying to override a method buide_profile which gives us Active Record Methods Added by has_one.
Methods Added by has_one
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
In your case you could re-write from_omniauth methods
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
logger.info "status=from_omniauth first_name=#{auth.info.first_name} first_last_name=#{auth.info.last_name}"
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.build_profile(first_name: auth.info.first_name)
#user.name = auth.info.name # assuming the user model has a name
#user.image = auth.info.image # assuming the user model has an image
end
end
Hope this help you!!!
Related
I am signing in users only from facebook with Omniauth
** Users::OmniauthCallbackController**
class Users::OmniauthCallbacksController <
Devise::OmniauthCallbacksController
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Facebook") if
is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to games_path
end
end
but when i try to get current_user to another contoller it always returns nil.
I have read that devise has a built in function for current_user but that didn t seem to work properly(also returned nil every single time) so i implemented another method.
current_user helper
before_action :define_current_user
def current_user
User.find_by id: session["current_user_id"]
end
helper_method :current_user
How can i make it work?
User.rb
class User < ApplicationRecord
belongs_to :team
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :rememberable, :trackable,:validatable
,:omniauthable, :omniauth_providers => [:facebook]
def self.new_with_session(params,session)
super.tap do |user|
if data = session["devise.facebook_data"] &&
session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
#Saving new user to the database
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do|user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.name = auth.info.name
user.image = auth.info.avatar
user.save!
end
end
end
Don't forget to add before_action :authenticate_user! callback to needed controllers. In this case you'll be able to get value of default current_user from Devise
I have a problem with both gems, because when I try to connect to my webpage with Facebook (Sign Up or Login) tell me they send a email confirmation or I need to confirm the email confirmation.
But the problem is that never send me a email, and other thing, if I don't want send a email when I Sign in my web across Facebook.
I put the code.
app/controllers/omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user 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_user_registration_url
end
end
def failure
redirect_to root_path
end
end
app/models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :omniauthable, :omniauth_providers => [:facebook]
validates :fullname, presence: true, length: {maximum: 50}
has_many :rooms
has_many :reservations
has_many :guest_reviews, class_name: "GuestReview", foreign_key: "guest_id"
has_many :host_reviews, class_name: "HostReview", foreign_key: "host_id"
def self.from_omniauth(auth)
user = User.where(email: auth.info.email).first
if user
return user
else
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.fullname = auth.info.name
user.image = auth.info.image
user.uid = auth.uid
user.provider = auth.provider
# If you are using confirmable and the provider(s) you use validate emails,
# uncomment the line below to skip the confirmation emails.
user.skip_confirmation!
end
end
end
end
I have Devise user auth included in my site and it is working well. I'm now trying to add Facebook authentication.
I'm able to login with FB, and the user is redirected back to my site ok. When I check the connected apps in FB settings I can see my site is there listed.
The problem is that the FB is not added to my user table. When I check the db, the provider and uid fields are blank, and site is still asking for the user to sign in.
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :validatable,
has_many :comments
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.name = auth.info.name # assuming the user model has a name
#user.image = auth.info.image # assuming the user model has an image
# If you are using confirmable and the provider(s) you use validate emails,
# uncomment the line below to skip the confirmation emails.
user.skip_confirmation!
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
Callback Controller
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user 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_user_registration_url
end
end
def failure
redirect_to root_path
end
routes.rb
#authentication
devise_for :users, path: "auth", controllers: {
sessions: 'users/sessions',
registrations: 'users/registrations',
unlocks: 'users/unlocks',
omniauth_callbacks: 'users/omniauth_callbacks'
}
User Session Controller
class Users::SessionsController < Devise::SessionsController
respond_to :js
before_action :configure_permitted_parameters, :if => :devise_controller?
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_in_params
devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute])
end
end
devise.rb
config.omniauth :facebook, '******', '*************', scope: "email", info_fields: 'email'
Update
Just caught the fact I have fname and lname fields in my user table, so have adjusted user.rb to reflect that:
user.fname = auth.info.fname
I'm having a little problem with Devise. When I try to login with an account, while I'm logged in with a different account, Devise is persisting the session of the first account I logged in with.
This happens when I log with via omniauth first, then try to log in manually with Devise.
here is my omniauth_controller
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
respond_to :json
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"]) if request.env["omniauth.auth"].present?
if #user && #user.persisted?
sign_in #user
cookies[:uId] = #user.id
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
end
redirect_to root_url
end
end
my user.rb file:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.name = auth.info.name # assuming the user model has a name
user.image = auth.info.image # assuming the user model has an image
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
This is because of the way devise is storing the data in your session. When you login manually it is storing the user_id versus when you use omniauth it stores other data in the session to know how to authenticate.
You can manually clear it out when the user visits the sign in/out pages:
class SessionsController < ApplicationController
before_action :clear_devise_session, only: [:new, :destroy]
private
def clear_devise_session
session['devise.facebook_data'] = nil
end
end
Granted it is recommended that a GET request should never manipulate state you may not want to do this on the new action and only the destroy action.
If this is just to test when developing you can run rake db:sessions:clear.
Pulling my hair out on this : being redirected to user/sign_up - here is my code:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :omniauthable, :omniauth_providers => [:facebook]
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.name = auth.info.name # assuming the user model has a name
## user.image = auth.info.image # assuming the user model has an image
end
end
end
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
omniauth = request.env["omniauth.auth"]
#user ||= User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
Rails.logger.level = 0
logger.debug "Session: #{#session.inspect}"
logger.debug "USer: #{#user.inspect}"
logger.debug "Omniauth: #{#omniauth.inspect}"
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user 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_user_registration_url
end
end
end
And here is the relevant logger output:
Session: nil
Processing by Devise::RegistrationsController#new as HTML
USer: #
Omniauth: nil
Redirected to http://secret-brushlands-1375.herokuapp.com/users/sign_up
Not sure what to make of this - the Facebook link is fine but my guess is the omniauth hash should not be nil and same of the session... ANy help appreciated.