I am trying desperately to find a way to get devise to work in my Rails 4 app. I have tried every tutorial I can find to get this set up.
The current tutorial is:http://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/
My previous bounty questions on related problems (commonly voted down & I don't understand why), show my other attempts at getting this done.
My problem currently is with this bit of text in the user model:
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
The problem is (pointing at this line: user = User.new) in the above method:
unknown attribute 'name' for User.
In the previous tutorial I tried, I discovered that linkedin worked when i changed the attributes to:
# self.email = omniauth['extra']['raw_info']['emailAddress']
# self.first_name = omniauth['extra']['raw_info']['firstName']
# self.last_name = omniauth['extra']['raw_info']['lastName']
The field names for the omniauth strategy are different to what's shown in the tutorial (at least for linkedin). But then if Facebook or twitter use different field names again, how does the tutorial solve for that?
Also, in my user table, I have attributes called first_name and last_name, but I tried changing the line to:
first_name: auth.extra.raw_info.'firstName',
That didn't work either.
When I try:
first_name: auth.extra.raw_info.name
I get this error:
undefined method `password_required?' for #<User:0x007fb7bc2ce6f8>
But anyway, I only want first name in that field and i think this is putting the whole name into first name (although Im not sure about that). Also, if this is going to be amended to work for linkedin, will that mean it will not work for Facebook and twitter?
It's all a big mess. I'm growing increasingly frustrated with this. Does anyone know how to solve this particular problem. I have been trying for 2.5 years to get devise/omniauth working.
My recent previous tutorial linked questions are:
Devise Omniauth - setup & defining strategies
Rails, Devise & Omniauth - problems with setup
There are several others, but I'm not figuring this out by my own efforts. I've had a few sessions on codementor.io but not been able to find help. A source of help would be greatly appreciated.
So trying the suggestion below, I tried changing the method in the user model to:
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
case auth.provider
when 'linkedin'
first_name: auth.extra.raw_info.firstName,
last_name: auth.extra.raw_info.lastName,
email: emailAddress ? email : "#{auth.uid}-#{auth.provider}.com",
#username: auth.info.nickname || auth.uid,
# email: email ? email : "#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
when 'facebook'
first_name: auth.extra.raw_info.first_name,
last_name: auth.extra.raw_info.last_name,
email: auth.extra.raw_info.email ? email : "#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
when 'twitter'
first_name: auth.extra.raw_info.nickname,
end
)
user.skip_confirmation!
user.save!
end
end
There are several problems with this, being:
unexpected ':', expecting keyword_end first_name: auth.extra.raw_info.firstName,
unexpected tLABEL, expecting '=' last_name: auth.extra.raw_info.lastName
unexpected tLABEL, expecting '=' email: emailAddress ? email : "#{au... ^
unexpected ',', expecting keyword_end
unexpected keyword_when, expecting keyword_end when 'facebook' ^
unexpected ':', expecting keyword_end first_name: auth.extra.raw_info.first_name, ^
unexpected tLABEL, expecting '=' last_name: auth.extra.raw_info.last_name,
unexpected tLABEL, expecting '=' email: auth.extra.raw_info.email ? ... ^
unexpected ',', expecting keyword_end
unexpected keyword_when, expecting keyword_end when 'twitter' ^
unexpected ':', expecting keyword_end first_name: auth.extra.raw_info.nickname, ^
unexpected keyword_end, expecting '='
I don't know how to solve any of this - I can't find any example (other than the one in the post below) of how to approach this - and that's clearly not working).
On top of all of the above, what do I do with Twitter? It has a nickname field. How can I separate the words out so the first word is saved as first_name and the 2nd word is saved as last_name?
Extrapolating from the particularly snarky comments below, I tried that suggestion again, with if statements. It still doesnt work.
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
if auth.provider = linkedin
first_name: auth.extra.raw_info.firstName,
last_name: auth.extra.raw_info.lastName,
email: emailAddress ? email : "#{auth.uid}-#{auth.provider}.com",
#username: auth.info.nickname || auth.uid,
# email: email ? email : "#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
end
if auth.provider = facebook
first_name: auth.extra.raw_info.first_name,
last_name: auth.extra.raw_info.last_name,
email: auth.extra.raw_info.email ? email : "#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
end
if auth.provider = twitter
first_name: auth.extra.raw_info.firstName,
end
)
user.skip_confirmation!
user.save!
end
end
Absent finding a solution to this on this board, I would appreciate advice on how much you think it would be reasonable to pay a professional to help resolve these problems.
I HAVE GONE RIGHT BACK AND COMMENTED OUT ALL OF THE CODE RELATED TO DEVISE AND OMNIAUTH AND NOW TRYING AGAIN, WITH THE DOC ON THE OMNIAUTH WIKI CALLED: MANAGING MULTIPLE PROVIDERS
It seems this doc may have typos in it that experienced coders can read past.
Currently, there is an error message being generated as follows:
/Users/config/routes.rb:35: syntax error, unexpected [, expecting keyword_do or '{' or '(' ...', to: 'sessions#create', via [:get, :post] ... ^ /Users/config/routes.rb:36: syntax error, unexpected [, expecting keyword_do or '{' or '(' match '/logout', to: 'sessions#destroy', via [:get, :post] ^
I have copied these routes directly from the user guide. I'm by far from an expert but I'm also confused about why '==' is used in some places and '=' is used in others in this doc. For example:
if signed_in?
if @identity.user == current_user
Whilst:
@identity.user = current_user
In the same method there is a variance.
I'm also confused about why the sessions controller doesnt inherit from the devise controller. In each of the other tutorials I have done that have had a sessions controller, it has inherited from devise.
There are quite a few other confusing aspects of this tutorial (like why doesnt it have a registrations controller, why are there no other routes for devise, why does the application controller have create and destroy methods)?
Desperately seeking help.
This is not a complete answer, but I have solved part of my problem.
The point of the oauth gem is to take the attributes from each social media strategy and unify them into a common form of expression.
By incorporating raw_info from the strategy, the code is not working with oauth. For example, linkedin auth hash returns data labelled 'firstName' where oauth recognises that as first_name. If you use
the attribute will be nil when linked is being called. This is already strange to me since LinkedIn gives basic profile details which suggest a label is first-name.
Anyway, the part I fixed is to remove reference to 'extra.raw' in the above line and use auth.info.first_name. That should resolve differences between strategy labels.
I am still working on the rest of the problems arising in this set up. Some of the issues in the sourcey tutorial are syntax, others are more substantial. I'll post again if I can sort them out.