I setup Facebook login with Devise and omniauth with these instructions https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
The Devise wiki gives some instructions for getting facebook info from the hash stored in this variable request.env['omniauth.auth'] See bottom for the hash.
For example, Devise wiki has these two methods for the User.rb model
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else # Create a user with a stub password.
User.create!(:email => data.email, :password => Devise.friendly_token[0,20])
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"]
end
end
end
So, using the hash below, I added the following to those two methods to get the name and image
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else # Create a user with a stub password.
User.create!(:email => data.email, :password => Devise.friendly_token[0,20], :name => data.name, :image => access_token.info.image) #I added access_token.info.image based on first answer
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"]
user.name = data["name"]
user.image = access_token.info.image #i changed this based on first answer below
end
end
end
Then in my view, I added the following to show the user name and image
<p>Name:<%= user.name %></p>
<p>Image: <%= image_tag user.image %>
However, only the name is showing. No image.
In my database, I have a name and an image column. The name from Facebook is being stored, but the image column says 'nil'
Any ideas how I can get the image to work?
Hash stored in request.env['omniauth.auth'] https://github.com/mkdynamic/omniauth-facebook/blob/master/lib/omniauth/strategies/facebook.rb#L31-47
info do
prune!({
'nickname' => raw_info['username'],
'email' => raw_info['email'],
'name' => raw_info['name'],
'first_name' => raw_info['first_name'],
'last_name' => raw_info['last_name'],
'image' => "#{options[:secure_image_url] ? 'https' : 'http'}://graph.facebook.com/#{uid}/picture?type=square",
'description' => raw_info['bio'],
'urls' => {
'Facebook' => raw_info['link'],
'Website' => raw_info['website']
},
'location' => (raw_info['location'] || {})['name'],
'verified' => raw_info['verified']
})
end
The image can be found at env["omniauth.auth"]["info"]["image"]. So in your case, access_token.info.image.
If you want to take a good look at the hash of nested hashes returned and see for yourself where everything is, put this as the first line of your callback controller:
render :text => "<pre>" + env["omniauth.auth"].to_yaml and return
EDIT: Ok, so here's what you need to do:
def self.find_for_facebook_oauth(omniauth)
if user = User.find_by_email(omniauth.info.email)
if omniauth.info.image.present?
user.update_attribute(:image, omniauth.info.image)
end
user
else # Create a user with a stub password.
User.create!(:email => omniauth.info.email,
:name => omniauth.info.name,
:image => omniauth.info.image,
:password => Devise.friendly_token[0,20])
end
end
As for the other method, if I'm not mistaken, it should look like this:
def self.new_with_session(params, session)
super.tap do |user|
if omniauth = session["devise.facebook_data"]
user.email = omniauth.info.email
user.name = omniauth.info.name
user.image = omniauth.info.image
end
end
end
But when is this method used? It's used by Devise when something goes wrong when creating your user. Imagine that the authentication provider doesn't give you an email (Twitter, for example, does this), what can you do? Well, you can redirect the user to your sign up page where he can complete the signup process. But if you redirect the user, you lose the data received by the oauth. The solution is to put this data into the session.
So in your controller, you should have something like:
if user.save
sign_in_and_redirect user, :event => :authentication
else
session["devise.facebook_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
Another problem, however, is that most of the times the data returned by the authentication provider is too big to fit in the session, so we have to pick exactly what we want to put in the session. Since you are only getting a name and an image, you can trim the extra info like so:
session["devise.facebook_data"] = env["omniauth.auth"].except('extra')
Related
I use Authlogic for authentication users + Omniauth for social authentication.
After creating new account for user, who signed up with Facebook, I want to set up his role in the system.
user model
def self.create_user_from_facebook(auth_hash)
a = self.create!({
:facebook_uid => auth_hash["uid"],
...,
:crypted_password => "facebook",
:password_salt => "facebook",
:persistence_token => "facebook",
:provider => auth_hash["provider"]
})
puts a.errors.inspect #returns no errors, record is successfully saved
puts a.inspect # returns: last_request_at: "2012-10-01 08:12:51"
Assignment.create(:user_id => user.id, :role_id => 2)
end
And when I try to add new record into assignments table this way, I get the error
undefined method `last_request_at' for #<Assignment:0x0000012a1539e0>
The column last_request_at is in the table users.
How does the column last_request_at from the table users relate with the table assignments? (with only these two columns: role_id, user_id)?
**EDIT** another information:
class UserOauthController < ApplicationController
def create
#current_user = User.find_or_create_from_oauth(auth_hash)
if current_user
UserSession.create(current_user, true)
flash[:success] = 'Successfully logged in.'
redirect_to root_url
else
flash[:warning] = 'Authorization ERROR! Please, try it again.'
redirect_to root_url
end
end
end
class User < ActiveRecord::Base
attr_accessible list of atteibutes
acts_as_authentic do |c|
authlogic set up
end
...validations...
def self.find_or_create_from_oauth(auth_hash)
provider = auth_hash["provider"]
uid = auth_hash["uid"].to_s
case provider
when 'facebook'
if user = self.where('email = ? and provider = ?', auth_hash["info"]["email"], provider).first
return user
elsif user = self.find_by_facebook_uid(uid)
return user
else
return self.create_user_from_facebook(auth_hash)
end
end
def self.create_user_from_facebook(auth_hash)
a = self.create!({
:facebook_uid => auth_hash["uid"],
...,
:crypted_password => "facebook",
:password_salt => "facebook",
:persistence_token => "facebook",
:provider => auth_hash["provider"]
})
puts a.errors.inspect
puts a.inspect
a.assignments.create(:role_id => MEMBER)
end
end
How can I fix it?
I'm new in rails and I'm trying to integrate omniauth and the login with google.
Surfing on the web i found a way to do it, but i got this error:
undefined method `[]' for nil:NilClass
app/models/user.rb:22:in `find_for_open_id'
app/controllers/users/omniauth_callbacks_controller.rb:17:in `open_id'
I think that is because i don't access correctly the access token information.
here is the code:
Model users.rb
def self.find_for_open_id(access_token, signed_in_resource=nil)
data = access_token['user_info']
if user = User.find_by_email(data["email"])
user
else # Create a user with a stub password.
User.create!(:email => data["email"], :password => Devise.friendly_token[0,20])
end
end
omniauth_callbacks_controller.rb
def open_id
# You need to implement the method below in your model
#user = User.find_for_open_id(env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.open:id_data"] = env["openid.ext1"]
redirect_to new_user_registration_url
end
end
devise.rb
config.omniauth :open_id, :store => OpenID::Store::Filesystem.new("/tmp"), :name => 'open_id', :identifier => 'https://www.google.com/accounts/o8/id'
I don't know what else i can show you.
For me the problem is here:
data = access_token['user_info']
access_token['user_info'] returns null.
Am I doing something wrong? Is there other way to access the 'user_info' and then the e-mail?
Thanks in advance. Hope i'm clear.
You should just be using data = access_token['info'] instead of data = access_token['user_info']
From there you have access to data['email], data['first_name'], and data['last_name'].
I followed the steps that are described in https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview and have a method in user model like this:
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = self.find_by_email(data.email)
user
else # Create a user with a stub password.
self.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
I intermittently get errors like
A NoMethodError occurred in omniauth_callbacks#facebook:
undefined method email' for "false":String
app/models/user.rb:138:infind_for_facebook_oauth'
that I haven't been able to reproduce. What is the source of this problem?
I am not sure what causes this either. Here's a work-around that simply creates a new object:
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if data == "false"
self.new
elsif user = self.find_by_email(data.email)
user
else # Create a user with a stub password.
self.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
The controller code shown in the example will then work - it will redirect the user to sign up.
I'm trying to save the name of a Facebook user upon saving that user, but I don't seem to be able to. I have followed the guides on the devise github and the integration with Facebook works fine; the users email is saved as is to be expected. However, I can't figure out how to save the users name. Right now, I do this:
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token['extra']['user_hash']
if user = User.find_by_email(data["email"])
user
else # Create a user with a stub password.
User.create(:email => data["email"], :name => data["name"], :password => Devise.friendly_token[0,20])
end
end
but that doesn't work. Am I doing something wrong?
Seems like Auth hash schema has changed a lot:
https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else
# Create a user with a stub password.
user = User.create!(:username => data.username ? data.username : data.nickname , :email => data.email, :password => Devise.friendly_token[0,20])
end
end
The users name is stored in:
auth = request.env['omniauth.auth'] #I think this is what your access_token variable equates to.
auth['user_info']['name']
If that is not what you need, I suggest you inspect the contents of the access_token.
I'm trying to build Facebook OAuth into my existing Authlogic login system. I have the OAuth part complete, and stored the facebook access_token. The problem I'm facing is to actually log the user in (create a session) without the user typing in their password.
#facebook's OAuth callback
def callback
access_token = client.web_server.get_access_token(params[:code], :redirect_uri => redirect_uri)
fb_user = JSON.parse(access_token.get('/me'))
#user = User.find_by_facebook_id(fb_user["id"]) || User.find_by_email(fb_user["email"]) || User.new
#user.update_attributes({
:facebook_id => fb_user["id"],
:first_name => fb_user["first_name"],
:last_name => fb_user["last_name"],
:gender => fb_user["gender"],
:email => fb_user["email"],
:timezone => fb_user["timezone"],
:locale => fb_user["locale"],
:facebook_url => fb_user["link"],
:facebook_access_token => access_token.token
}) #unless #user.updated_at < 2.days.ago
# TODO: set current_user
# Maybe something like this?
# #user_session = UserSession.new({
# :remember_me => true,
# :password =>"[FILTERED]",
# :email => email
# }).save
flash[:success] = "Welcome, #{#user.name}"
redirect_to :root
end
Nevermind I figured it out. It was in the README the whole time.
UserSession.new(#user, true) //true = persistent session