Facebook sends invalid userdata over Omniauth + Devise - ruby-on-rails

Just two failed login attemps hit my mailbox. Looking at the data the problem was found quickly: Facebook is sending a 11 digit number instead of a email address to my Omniauth-Controller. So the User-Model can't be saved, because its created with a email constraint on the column in the migration file.
Anybody has a idea how I should proceed with the data in such cases? I don't really care if the user has a malformed email address, so I could theoretically just change the column to a normal string etc. Are there better solutions?
Bonus Questions:
Any other fields known to be unrelieable with the facebook + omniauth + devise
environment?
Any fields known to be unrelieable with the
google_oauth2 plugin?

I take it you are using a function similar to below...
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
Are you sure auth.info.email is mapped to the correct parameter
EDIT:
Since the above is all fine. I guess the two options are remove the contraint as you say or perform your own check for string or that it contains an # or whatever and if the data passed is not the correct format you could insert a dummy email address.

This is how i solved it quick & dirty:
# facebook can send malformed/invalid email
email = auth[:info][:email]
unless email =~ /^(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+#((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})$/i
email = "#{Time.now.to_i}#MALFORMED_EMAIL.com"

Related

Update devise user password using a custom method

I am using Devise for authentication and trying to change the password of certain users to their date of birth using a method
def set_dob_password id
#user = User.find(id)
#user.update_attribute(password: #user.birth_date)
end
isn't working
. What is the best way of doing this ?
Of course it won't work! Devise stores encrypted passwords only in the DB. If you look at the users table you won't see a 'password' field but 'encrypted_password' column instead.
You first have to encrypt the password.
pw = BCrypt::Password.create(#user.birth_date)
#user.update_attribute(:encrypted_password, pw)
Make sure you have the 'bcrypt' gem first.
#user.update_attributes(password: params[:password], password_confirmation: params[:password_confirmation]). You need to update both password + password confirmation. So in your case replace parmas with user DoB.

Omniauth-Facebook giving error in callback - Email can't be blank?

Hi my devise login appears to work, but for some reason Facebook is not sending me the email in my API request. Then I am getting this error:
I read up on the July 18th, 2015 adjustment and added scope. No luck. Here is the initializer:
config.omniauth :facebook, ENV['FACEBOOK_APP_KEY'],
ENV['FACEBOOK_APP_SECRET'], scope: 'email', info_fields:'email,name'
Maybe I'm missing something on the FB Developer Page?
You will have to handle it :
Some Facebook users have unverified emails, so Facebook will not give it to you
There is Facebook users with only telephone numbers and no email
Facebook allow the user to not provide some information to your app, for example the email
It happens with profiles with unverified emails and when user choose not to provide your app with their email
rescue that error and redirect user to normal sign up page so they continue with providing their email address
Ok, if what Nicolas is saying is correct then you can easily solve this problem. Before save populate some fake and unique email with "+":
Add to your User model something like this:
class User < ActiveRecord::Base
before_validation :adjust_email, on: :create
private
def adjust_email
self.email = "fake+#{generate_token}" if email.blank?
true
end
def generate_token
rand(36**8).to_s(36)
end
end
And you will have to show your user this fake email, so that she will be able to login with it later. A downside of this solution is that you will not be able to send emails to such users, but maybe you don't want to.
What Remon suggested is also a valid option to redirect to sign up in case you get this particular error.
So, it all depends on what you actually plan to do with the email.

Devise + Omniauth-Facebook get user's phone #

I'm using Devise for authentication in my Rails 3.2 app and am having trouble configuring omniauth-facebook to get the new user's phone number.
First of all: I'm not even sure that it's possible to get a phone number, so if that's the case and someone knows for sure, I'd be happy just for a confirmation.
It doesn't appear from https://github.com/mkdynamic/omniauth-facebook that "phone" is part of the FB auth hash by default, though the general Omniauth schema does have .info.phone (not required, of course). So my first idea was that it's a FB permissions problem. I'm not sure what permissions to use, though, as the Permissions with Facebook Login page doesn't say where to find the phone value (maybe this means it's just not possible?).
I have phone as a required attribute on the User model, so when I try to get it through FB the new object never persists. It works fine without looking for a phone number.
My config file:
# /config/initializers/devise.rb
config.omniauth :facebook, 'FACEBOOK_APP_ID', 'FACEBOOK_APP_SECRET', scope: 'email,public_profile', display: 'page'
In my user model:
# user.rb
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.first_name = auth.info.first_name
user.last_name = auth.info.last_name
user.phone = auth.extra.raw_info.phone # have also tried auth.info.phone
end
end
Thanks in advance for any help you may be able to provide!
There's currently no way to get a user's phone number from Facebook.
Facebook's Graph API reference lists all the user information that you can access via the API. A user's phone number is not on the list.
Also, to inspect the contents of the auth hash, add the following line of code at the beginning of your authentications/sessions/callbacks controller action:
render :text => "<pre>" + env["omniauth.auth"].to_yaml and return
You'll see that there is no phone field at auth.extra.raw_info or auth.info.

Devise - create user account with confirmed without sending out an email?

I integrated devise with facebook. Now when I create a user account after the user has logged in with his/her facebook account,
user = User.create(:email => data["email"],
:password => Devise.friendly_token[0,20])
user.confirmed_at = DateTime.now
user.save!
even though the account has been confirmed, an confirmation email is still fired. Any idea how I can turn the email firing off?
The confirm callback happens after create, so it's happening on line 1 of your example, before you set confirmed_at manually.
As per the comments, the most correct thing to do would be to use the method provided for this purpose, #skip_confirmation!. Setting confirmed_at manually will work, but it circumvents the provided API, which is something which should be avoided when possible.
So, something like:
user = User.new(user_attrs)
user.skip_confirmation!
user.save!
Original answer:
If you pass the confirmed_at along with your create arguments, the mail should not be sent, as the test of whether or not an account is already "confirmed" is to look at whether or not that date is set.
User.create(
:email => data['email'],
:password => Devise.friendly_token[0,20],
:confirmed_at => DateTime.now
)
That, or just use new instead of create to build your user record.
If you just want to prevent sending the email, you can use #skip_confirmation_notification, like so:
user = User.new(your, args)
user.skip_confirmation_notification!
user.save!
See documentation
Skips sending the confirmation/reconfirmation notification email
after_create/after_update. Unlike #skip_confirmation!, record still
requires confirmation.
Open up the Rails console
rails c
Note the user (through id) or using rails helper methods, eg. first, last.
Create a variable to hold the user.
user = User.last
Use the skip_confirmation helper to confirm the user, then save.
user.skip_confirmation
user.save

Rails Devise: Set password reset token and redirect user

In my app for a certain use case I create a new user (programmatically set the password) and send them a confirmation email.
I would like them to be able to change their password immediately after confirming (without having to enter the system generated one which I don't want to send them)
In effect I would like
1) System creates a new user account with generated password.
2) System sends confirmation email.
3) User clicks confirmation and is redirected to enter in their password (effectively send them to a URL like below)
Change my password
Any help / pointers would be great.
A simple way to have just one step for users to confirm email address and set initial password using the link you proposed...
Send one email your app generates, including a reset_password_token, and consider user's possession of that token confirmation of the validity of that email address.
In system account generation code, assuming User model is set up with :recoverable and :database_authenticatable Devise modules...
acct = User.new
acct.password = User.reset_password_token #won't actually be used...
acct.reset_password_token = User.reset_password_token
acct.email = "user#usercompany.com" #assuming users will identify themselves with this field
#set other acct fields you may need
acct.save
Make the devise reset password view a little clearer for users when setting initial password.
views/devise/passwords/edit.html.erb
...
<%= "true" == params[:initial] ? "Set your password" : "Reset your password" %>
...
Generated Email
Hi <%= #user.name %>
An account has been generated for you.
Please visit www.oursite.com/users/password/edit?initial=true&reset_password_token=<%= #user.reset_password_token %> to set your password.
No need to include :confirmable Devise module in your User model, since accounts created by your app won't get accessed without the reset_password_token in the email.
Devise will handle the submit and clear the reset_password_token field.
See devise_gem_folder/lib/devise/models/recoverable.rb and database_authenticatable.rb for details on reset_password_token method and friends.
If you want to use Devise :confirmable module rather than this approach, see the Devise wiki page.
In Rails 4.1, the following modification of Anatortoise House's reply works:
user = User.new
user.password = SecureRandom.hex #some random unguessable string
raw_token, hashed_token = Devise.token_generator.generate(User, :reset_password_token)
user.reset_password_token = hashed_token
user.reset_password_sent_at = Time.now.utc
user.email = 'user#usercompany.com'
user.save!
# Use a mailer you've written, such as:
AccountMailer.set_password_notice(user, raw_token).deliver
The email view has this link:
www.oursite.com/users/password/edit?initial=true&reset_password_token=<%= #raw_token %>
Here is my snippet for mailer preview
class Devise::MailerPreview < ActionMailer::Preview
def reset_password_instructions
user = User.last
token = user.send(:set_reset_password_token)
Devise::Mailer.reset_password_instructions(user, token)
end
end
You can call
user.send(:set_reset_password_token)
It may not be stable, as it's a protected method but it can work for your case. Just cover it with a test.
(tested on Devise v. 3.4)

Resources