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
Related
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 have a rails app and I want to make a Github connect. I have followed the devise tutorial about Facebook and adapted it for Github.
I have this error on the callback, when accessing /users/auth/github/callback?code=xxxxxxx
ActionController::RoutingError (uninitialized constant Users):
I know that other posts are related to this issue but their answer do not fix my problem.
routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def github
# 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 => "Github") if is_navigational_format?
else
session["devise.github_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
models/user.rb
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 => [:github]
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]
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.github_data"] && session["devise.github_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
end
I can't get devise omniauth-facebook to work. I've followed the devise guide to no avail. I think my problem is the model is not retrieving info.
Model
class User < ActiveRecord::Base
has_attached_file :image, styles: {large: "1920x1080#", medium: "800x500#", thumb: "100x100"}, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
def admin?
admin
end
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
puts request.env["omniauth.auth"]
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
Routes.rb
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
devise_for :users do
delete 'logout' => 'sessions#destroy', :as => :destroy_user_session,
:controllers => { :omniauth_callbacks => "user/omniauth_callbacks" }
end
Controller
class User::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# You should configure your model like this:
# devise :omniauthable, omniauth_providers: [:twitter]
# You should also create an action method in this controller like this:
# def twitter
# end
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 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
After I click "Login with facebook", it directs to a facebook url, I input a password, press enter, get redirected to the same page with a long url. (http://localhost:3000/demographics?code=AQDK1z40APoLiWykomxDDUljBUNHotenM4lzj_bZMhH8iQ74J_Nu_EUnPqBqkbNAeWQEPZwQs7YghqkB4eD7AoQLkN_RuYIlmotMtrJc4UyGRSe3CJIHcxp6kcB9BuYHA_Ldz0NMJvvGzOuvC-uDpFn6TyrzvV5v9LvivORXVduSsCy7_r6PcW8jxAkWqZzKyASXf26h8h3f_kha2d0KX6Ygft8ozN1HT9Xr-1y7ZtIKgTXEGMrqK950kASv2oTE0tQ5CYt6mfEZsVyLpykYIApOls8NLhjOaOIJewzV9EnLdSq0FbrvtedhhDmy-hg6IkRAbRVgwEkfUFsi9DXoxKyX&state=bdbf498f33f67ef57f3f54b846f870f21bb80c039c099f1a#=)
EDIT:
Now I am getting
Could not authenticate you from Facebook because "Invalid credentials".
So the problem was I didn't config the routes properly.
In fact, I had to remove all existing devise routes and add this line.
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
This is what worked for me:
user model
devise :omniauthable, :omniauth_providers => [:facebook]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.username = auth.info.name #gives full user name
user.password = Devise.friendly_token[0,20]
user.skip_confirmation!
user.save
end
end
devise.rb
config.omniauth :facebook, ENV['facebook_key'], ENV['facebook_secret'],
scope: 'email,public_profile', info_fields: 'email, first_name, last_name'
callbacks_controller.rb
class CallbacksController < ApplicationController
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
flash[:notice] = "Logged in as #{#user.username}"
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
routes.rb
devise_for :users, controllers: { omniauth_callbacks: "callbacks" }
The problem is Facebook is not always returning email for user
from facebook developers https://developers.facebook.com/bugs/298946933534016
Some possible reasons:
No Email address on account
No confirmed email address on account
No verified email address on account
User entered a security checkpoint which required them to reconfirm
their email address and they have not yet done so
Users's email address is unreachable
Set an condition in your controller if request.env["omniauth.auth"].info.email.present? see the script below.
class User::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
puts request.env["omniauth.auth"] # check if request.env["omniauth.auth"] is provided an email
if request.env["omniauth.auth"].info.email.present?
#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
else
redirect_to new_user_registration_url, notice: "Couldn't connect to your #{request.env["omniauth.auth"].provider} account. Try to sign up."
end
end
end
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!!!
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.