I'm with a doubt here. I am using the gem ' omniauth - facebook ' and could bring up on the screen the name of the User , photo and email. I would like to get new data as gender for example. Dai created a migration add the field to the users table , put the method in view , all right. However I can not figure out how to write the method name and is giving indisponivel method . Does anyone have any ideas? I know that the error is in the method name but not know how to call it. Thanks.
Error Stack Trace
Erb Code Block
# app/models/user.rb
class User < ActiveRecord::Base
def self.omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create
do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.image = auth.info.image
user.email = auth.info.email
user.public_profile.gender = auth.public_profile.gender
user.token = auth.credentials.token
user.expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
It seems like you've created your model based on a tutorial and that tutorial is out of date for facebooks current API, which you can see here
Facebook Auth Hash, complete
So in this, you're looking for gender in under info, but it's under extra->raw_info - meaning your code would look more like:
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.image = auth.info.image
user.email = auth.info.email
user.public_profile.gender = auth.extra.raw_info.gender
user.token = auth.credentials.token
user.expires_at = Time.at(auth.credentials.expires_at)
user.save!
Related
I am running rubocop on rails and it gave me the message below.
W: Shadowing outer local variable - user.
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
^^^^
This is the code.
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.fullname = auth.info.name
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.image = auth.info.image
user.password = Devise.friendly_token[0, 20]
end
end
end
This means that the user provided as a block arguments will overwrite the user variable defined here user = User.where(email: auth.info.email).first
To overcome it you'll need to change the name of one of the variables.
Either something like:
result = User.where...
return result if result
Or:
where(provider: auth.provider, uid: auth.uid).first_or_create do |u|
u.fullname = auth.info.name
...
end
Some more info: https://github.com/bbatsov/ruby-style-guide#no-shadowing
I'm able to get the Facebook avatar through Omniauth, but I'm only able to resize the default image not the Facebook image which is 50 x 50px by default. How can I resize it to 38px?
Here's my application helper:
module ApplicationHelper
def avatar_url(current_user)
if current_user.avatar.present?
current_user.avatar
else
gravatar_id = Digest::MD5.hexdigest(current_user.email.downcase)
"http://gravatar.com/avatar/#{gravatar_id}.png?s=28&d=mm"
end
end
end
and my User class:
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(auth.slice(:sprovider, :uid)).first_or_create do |user|
user.sprovider = auth.provider
user.uid = auth.uid
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.email = auth.info.email
user.cell_phone = auth.info.cell_phone
user.avatar = auth.info.image
end
end
I have the following code in user.rb using the Facebook-omniauth gem that logs in a single user:
def self.from_omniauth(auth)
where(auth.slice(:provider, :fb_id)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.fb_id = auth.uid
user.name = auth.info.name
user.first_name = auth["info"]["first_name"] unless auth["info"].blank?
user.last_name = auth["info"]["last_name"] unless auth["info"].blank?
user.picture_url = auth.info.image
user.email = auth.info.email
user.oauth_token = auth.credentials.token unless auth["info"].blank?
user.location = auth.info.location unless auth["info"].blank?
user.save!
end
I want to build user accounts for multiple facebook users. When I log its fine but when another person signs in, the app replaces my user instance. Is there a way to keep building user accounts from this gem?
Thanks!
The solution was in the second line at: where(auth.slice(:provider, :fb_id))
I changed it from :uid to :fb_id to match the facebook id of everything else in all my other tables but after inspecting the auth hash I realized that :fb_id was not found at all and thus wasn't producing any new Users.
I renamed my User fb_id column back to uid and it now collects new users properly:
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.name = auth.info.name
user.first_name = auth["info"]["first_name"] unless auth["info"].blank?
user.last_name = auth["info"]["last_name"] unless auth["info"].blank?
user.picture_url = auth.info.image.sub("square", "large")
user.email = auth.info.email
user.oauth_token = auth.credentials.token unless auth["info"].blank?
user.location = auth.info.location unless auth["info"].blank?
user.save!
end
end
I am using omniauth-facebook gem with devise to authenticate with Facebook in my rails application in my user model
def self.from_omniauth(auth)
# immediately get 60 day auth token
oauth = Koala::Facebook::OAuth.new("App Key", "App secrets" )
new_access_info = oauth.exchange_access_token_info auth.credentials.token
new_access_token = new_access_info["access_token"]
new_access_expires_at = DateTime.now + new_access_info["expires"].to_i.seconds
begin
where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.first_name
user.lastname =auth.info.last_name
user.email =auth.info.email
user.authentication_token = new_access_token
user.oauth_expires_at = new_access_expires_at
user.save!
end
rescue ActiveRecord::RecordInvalid
end
end
#interact with facebook
def facebook
#facebook ||= Koala::Facebook::API.new(authentication_token)
block_given? ? yield(#facebook) : #facebook
rescue Koala::Facebook::APIError => e
logger.info e.to_s
nil # or consider a custom null object
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
and on my omniauth_callbacks controller I have this method:
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed 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
These methods are used to authenticate user from scratch via Facebook. I need a way to connect existing users with their Facebook accounts not new ones if they registered via normal devise registration method
When an existing user is trying to sign in via Facebook the following error occurs:
A `NoMethodError` occurred in `omniauth_callbacks#facebook`:
undefined method `persisted?' for nil:NilClass
app/controllers/omniauth_callbacks_controller.rb:4:in `all'
You can find user by email first and just update provider and uid fields in case he is already exists.
So your User.from_omniauth may looks like that:
def self.from_omniauth(auth)
# immediately get 60 day auth token
oauth = Koala::Facebook::OAuth.new("", "" )
new_access_info = oauth.exchange_access_token_info auth.credentials.token
new_access_token = new_access_info["access_token"]
new_access_expires_at = DateTime.now + new_access_info["expires"].to_i.seconds
user = where(provider: auth.provider, uid: auth.uid).first
unless user
# in that case you will find existing user doesn't connected to facebook
# or create new one by email
user = where(email: auth.info.email).first_or_initialize
user.provider = auth.provider # and connect him to facebook here
user.uid = auth.uid # and here
user.username = auth.info.first_name
user.lastname = auth.info.last_name
user.authentication_token = new_access_token
user.oauth_expires_at = new_access_expires_at
# other user's data you want to update
user.save!
end
user
end
upd:
In case you faced password validation error you can override User#password_required? method to skip validation for user's signed in via Facebook.
That behavior described in following episode of RailsCasts
Just for add an updated version of the answer for rails 4 and devise 3.4.x
Like this is how an updated omniauth would look like
def self.from_omniauth(auth)
if !where(email: auth.info.email).empty?
user = where(email: auth.info.email).first
user.provider = auth.provider # and connect him to facebook here
user.uid = auth.uid # and here
user.save!
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.first_name = auth.info.name # assuming the user model has a name
user.avatar = process_uri(auth.info.image) # assuming the user model has an image
end
end
end
Just look for user by email, if there is one, just add the provider and uid, if there is not one, just create it as suggested in the documentation
You can have your from_omniauth be something like this:
def self.from_omniauth(auth)
where(auth.info.slice(:email)).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.username = auth.info.name
user.description = auth.extra.raw_info.bio
end
end
In this way, if there's an existing user with email same as the facebook account, then the first_or_create will return it and then the user can sign in (it won't be update).
Thanks to this other question I have amended my scopes so now I am requesting the correct permisisons so that I can save a users gender and location to the DB.
This is my user.rb code where all the info bar gender and location are saved. After following the protocol set by auth.info.name etc neither gender or location are saved or even retrieved.
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.name = auth.info.name
user.email = auth.info.email
user.picture = auth.info.image
user.gender = auth.info.gender
user.country = auth.info.locale
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
here are the scopes im sending in
Rails.application.config.middleware.use OmniAuth::Builder do
provider :youtube, YOUTUBE_KEY, YOUTUBE_SECRET, { :scope => "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://gdata.youtube.com", access_type: 'offline', approval_prompt: '' }
end
routes
match 'auth/youtube/callback', to: 'sessions#create'
Does anyone know the correct way to save these 2 values?
The problem here is that you are looking for that info in the wrong place in the omniauth hash. If you add the following line of code to the beginning of your controller action:
render :text => "<pre>" + env["omniauth.auth"].to_yaml and return
You'll be able to inspect the contents of the hash returned by the OAuth provider and see that gender and locale are located inside the extra key of the hash.
So, the answer here is just changing the two offending lines to:
user.gender = auth.extra.raw_info.gender
user.country = auth.extra.raw_info.locale