Im using devise with my rails 4 app. I authenticate with Facebook, LinkedIn and email.
I've just started to use Figaro and the only change I have made to my code is to swap the password that I use for my email account out of the production.rb into my application.yml file.
Now, when I test the LinkedIn registration link, I get an error saying that something went wrong (after pressing "Register with LinkedIn"). I get the same error when I try to authenticate with the other options.
I have a callback error in my omniauth callback controller for linkedin. The line with the problem is the '#user.send_admin_email' below:
def linkedin
#user = User.find_for_linkedin_oauth(request.env["omniauth.auth"])
if #user.persisted?
#user.send_admin_mail
redirect_to root_path, :event => :authentication
# sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
# set_flash_message(:notice, :success, :kind => "LinkedIn") if is_navigational_format?
else
session["devise.linkedin_data"] = request.env["omniauth.auth"]
redirect_to root_path
end
end
I have a mailer set up which sends me an email to tell me when there is a new registration. It uses the email address for which I moved the password from production.rb to application.yml
Does anyone know how to resolve this error?
Thank you very much
It is possible that the problem is the text-encoding of your application.yml file, especially if your email password has non-standard characters in it. Try firing up the console, and just output your password as it looks with to_s
p ENV['your_figaro_key'].to_s
See if this returns what you would expect
Hmm, it seems a "strong parameter" problem to me. Check your "send_admin_mail" to see if there is a "update_atributes" (or a "save").
My guess is that your user.rb is something like:
class User < ActiveRecord::Base
...
attr_accessor :extras
def self.find_for_linkedin_oauth(access_token, signed_in_resource=nil)
...
user = User.where(...)
...
user.extras = access_token.extras
...
end
def send_admin_mail
...
self.update_attributes(some_attribute: extras["some_attribute"])
...
end
If you are doing this, "save" will try to do an UPDATE with a non-permitted parameter. The correct version must be something this:
self.update_attributes(some_attribute: extras.permit(:some_attribute))
If I'm not wrong, the first implementation worked in the previous versions of strong parameters, but not anymore.
Related
I've spent the last day trying to fix this issue and it's driving me nuts.
Last night, I had facebook login working on my site and was retrieving basic user info. My problems started when I added :scope => 'user_birthday' to config/initializers/omniauth.rb which now looks like this
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, "APP_ID", "APP_SECRET", :scope => 'user_birthday'
end
For reference, I've removed the line config.omniauth :facebook, "APP_ID", "APP_SECRET" from config/initializers/devise.rb
I spent hours trying to get this to work but had to give up eventually. Then this morning I ran it again and it worked. Overjoyed, I tried to add another parameter to :scope but now the whole thing is broken again. I can get it to work if I remove the :scope but when I put it back in it fails every time (even if it's just :scope => 'user_birthday' like I had working first thing this morning).
To locate the problem, I put debug code in omniauth_callbacks_controller.rb and it now looks like:
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"])
puts "start before persist debug"
puts #user.birthday
puts #user.persisted?
puts "end before persist debug"
if #user.persisted?
puts "Start persisted debug"
puts request.env["omniauth.auth"]
puts "End debug"
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"]
puts "Start unpersisted debug"
puts request.env["omniauth.auth"]
puts "End debug"
redirect_to new_user_registration_url
end
end
end
This debug clearly shows that facebook is returning the necessary information but the app is failing because .persisted? is returning false and so I get re-directed to the new_user_registration page which returns the following:-
NoMethodError in Devise::Registrations#new
Showing /home/action/workspace/cloudapp/app/views/devise/shared/_links.html.erb where line #23 raised:
undefined method `omniauth_authorize_path' for #<#<Class:0x007f3aeffff038>:0x007f3aefffdf08>
I can't for the life of me figure out why .persisted? is returning false. I'm using Nitrous.io for development with a Heroku postgresql database. I've confirmed there are no users in the database by running
rails c
User.all
This returns:
User Load (89.4ms) SELECT "users".* FROM "users"
=> #<ActiveRecord::Relation []>
I have a feeling the problem is in models/user.rb but I can't figure out how to debug it to see if it's finding a user and therefore not persisting or trying to create one and failing. Does anyone know a simple way to debug this?
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.birthday = auth.extra.raw_info.birthday
# user.image = auth.info.image # assuming the user model has an image
end
end
I've gone over everything about 50 times and am close to giving up.
The only thing I can think of is that where(provider: auth.provider, uid: auth.uid) is returning something (which it shouldn't because my database is empty). Would there possibly be an index that exists somewhere outside my database and that's what it's searching?
Please, for my sanity, can anyone help? If you need more info I'll gladly provide it
Edit 1
Just tried the following and it works which make me more confused than ever:
Delete the app from my facebook account as I'm testing using that account
Try to log in with facebook with :scope => 'user_birthday' left in. Facebook lists the permissions sought as your public profile and birthday. Accept and get sent back to my site which fails as per above (even though the info is definitely being sent back)
Remove :scope => 'user_birthday' and try log in using facebook again. Get directed to facebook which lists permission sought as your public profile and email address. Accept and get directed back to site which now works and also has the user birthday stored and accessible because I had the permisision from facebook from number 2 above.
I'm completely at a loss now
To find out about why is the object not being saved. You need to puts the errors.
puts #user.errors.to_a
And to check the content of the auth
puts request.env["omniauth.auth"]
I had the same problem and follow the answer above and I put "#user.errors.to_yaml" on my code to I see where was the error and I found.
I am using "devise" and "omniauth-facebook" too. The default scope of the omniauth-facebook is "email". However, I put on the scope the properties: "user_about_me, user_status, user_location, user_birthday, user_photos". I need to add "EMAIL" on the scope to devise to use on creation of the 'user'. I discover this when I saw my error: "email don't be blank".
Summary:
If you insert properties on the scope, ALWAYS put "email" too.
Facebook 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
You also need the 'email' extended permission, even for users who have a valid, confirmed, reachable email address on file.
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
class Users::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: "Can't sign in to your #{request.env["omniauth.auth"].provider} account. Try to sign up."
end
end
end
I've seen a couple people ask a similar question, but I really need advice on how to debug this issue. I'm trying to setup facebook connect using Devise using the article here: https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
Every time I click on the login with facebook link, I get the blank page that just says: Not found. Authentication passthru. Clearly, there is no JavaScript/ajax setup on the prior page to pull up the facebook login screen.
I know this can work on my system, as I made a blank project with the exact same code from the link above and it works. Of course, my project is much bigger with lots of code, so I'm trying to figure out what in my project is causing this not to fire.
Any help on how to debug is appreciated.
Thanks!
This is the #passthru code from the devise source.
def passthru
render :status => 404, :text => "Not found. Authentication passthru."
end
Which means that devise is unable to recognize your facebook callback. Make sure you setup up your callback controller properly or post your user controller code.
I ran into the same error. I was missing the Facebook callback controller (app/controllers/users/omniauth_callbacks_controller.rb):
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# ...
def facebook
respond_to do |format|
format.html {
#user = User.find_for_facebook(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user
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
end
This code references the method "find_for_facebook" in my user model (app/models/user.rb):
class User < ActiveRecord::Base
# ...
def self.find_for_facebook(auth_hash)
user = User.where(:email => auth_hash.info["email"]).first
unless user
user = User.create(
username: auth_hash.info["nickname"],
email: auth_hash.info["email"],
password: Devise.friendly_token[0,20])
end
user.provider = auth_hash["provider"]
user.uid = auth_hash["uid"]
user
end
end
Make sure to restart your development server so all the changes get picked up.
I'm using devise 2.0 and gem omniauth-twitter
The problem is that twitter does not send an email in response, so the model user of my application validates that there is an email and I get the following error in the callback:
Email can't be blank
I have this in my user.rb model:
#config omniauth twitter
def self.find_for_twitter_oauth(access_token, signed_in_resource = nil)
data = access_token.extra.raw_info
if user = User.where(:username => data.screen_name).first
user
else
User.create!(:username => data.screen_name, :password => Devise.friendly_token)
end
end
and I have this in my omniauth_callbacks_controller.rb
def twitter
#user = User.find_for_twitter_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Twitter"
sign_in_and_redirect #user, :event => :authentication
else
# http://stackoverflow.com/questions/7117200/devise-for-twitter-cookie-overflow-error
session["devise.twitter_data"] = request.env["omniauth.auth"].except('extra')
redirect_to new_user_registration_url
end
end
I want force to user to give a email address for send newsletter, advertirser...etc
How can I fix this problem?
Thank you!
Add this to your User model:
def email_required?
super && provider.blank?
end
You can use an equivalent method for the password:
def password_required?
super && provider.blank?
end
This should override the field requirements when using Omniauth providers.
I am facing the same problem, Twitter doesn't give you the email via oauth, you have to think in another way to obtain the email. The only solution that I figured out, is override the callback and use the twitter data to autocomplete a form and give the user the chance to complete his mail by himself and then sign up and save the user in the database.
Look the following question:
Is there a way to get an user's email ID after verifying his/her Twitter identity using OAuth?
A solution is explained thoroughly here:
http://asciicasts.com/episodes/236-omniauth-part-2
And on GitHub:
https://github.com/fertapric/rails3-mongoid-devise-omniauth/wiki/How-To:-Retrieve-email-information-(middle-step)-from-providers-like-Twitter-or-LinkedIn
For some reason, this question shows up higher in Google's ranks than these links, so I thought I'd add them to the answers.
You need to remove the presence validation of the email field.
Follow this post and see if that works.
Sorry for the late response, hope this helps.
I'm using omniauth-facebook gem with rails 3.2 and devise 2.0.
I have a website with 2 languages, english and spanish.
http://localhost:3000/en
http://localhost:3000/es
The gem works fine for english users because in omniauth_callbacks_controller.rb the redirect go to http://localhost:3000/en
This is my omniauth_callbacks_controller.rb for facebook:
def facebook
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
Then problem is for spanish users. that if they use http://localhost:3000/es the redirect from callback go to http://localhost:3000/en
I want that redirect from callback go to specific language that is using that user.
How can I do it?
Thank you!
I had similar issue, and did not find a way to change the callback url, but I could set the locale in the method when the callback happens.
The original url (which has the orignal correct locale) is in stored in request.env['omniauth.origin']
So in facebook method pick the locale from the original url, where it is in similar way the two letters after the domain part.
I added in the beginning of the facebook method:
I18n.locale = exctract_locale_from_url(request.env['omniauth.origin']) if request.env['omniauth.origin']
Where the exctract_locale_from_url is a horrible looking regexp :)
def exctract_locale_from_url(url)
url[/^([^\/]*\/\/)?[^\/]+\/(\w{2})(\/.*)?/,2]
end
I think you must extract your omniauth config to yaml file and insert "#{i18n.locale}" to the end of callback link.
I'm using omniauth and devise to log users in with their facebook acounts, everything works however when I try to pull their email out of the hash I get this error:
NoMethodError in AuthenticationsController#create
undefined method `id' for "/":String
Here's the full log of the error : http://pastie.org/1698569
The error goes away and it lets me log in just fine after I refresh!
EDIT: It turns out that its line 22 in my authentications controller
sign_in_and_redirect_to(:user, root_path)
For some reason after running this method I can't sign_in the :user
def apply_facebook(omniauth)
if (extra = omniauth['extra']['user_hash'] rescue false)
self.email = (extra['email'] rescue '')
end
end
However, if I don't run that method, then it can sign_in_and_redirect_to just fine
Here's my controllers/model http://pastie.org/1698453
Really appreciate any help
You can't use root_path as the second parameter for sign_in_and_redirect . Here are some available ways you can use it:
sign_in_and_redirect :user, #user # sign_in(scope, resource)
sign_in_and_redirect #user # sign_in(resource)
sign_in_and_redirect #user, :event => :authentication # sign_in(resource, options)
sign_in_and_redirect #user, :bypass => true # sign_in(resource, options)
Since your second parameter isn't either a resource or options (it's a string), you're getting an error. You need to change it to:
sign_in_and_redirect(:user, user) # based on your pastie
If you want to customize the return path to force it to go to a different URL after sign-in, you can do something like this in your ApplicationController:
def after_sign_in_path_for(resource)
"/go/to/this/path"
end