How to bypass Twitter omniauth "email can't be blank" error? - ruby-on-rails

I am having a little trouble with Twitter authentication. I keep getting the email can't be blank, and redirect to sign up, but after inserting email and clicking sign up, I still get the same error. I tried making it unneeded, but I get the error saying that someone with "" email exists already.
Thanks.
Devise Routes.rb
def has_role?(role)
return true;
end
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.nickname
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new(session["devise.user_attributes"]) do |user|
user.attributes = params
user.valid?
end
else
super
end
end
def password_required?
super && provider.blank?
end
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
end
Call Back Controller
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
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 :twitter, :all
end

You can define username to be your "authentication key" instead of email by
uncommenting this line
config.authentication_keys = [ :email ]
in config/initializers/devise.rb
and changing it to
config.authentication_keys = [ :username ]

i also had the same problem.
this error arises due the fact that in devise User model Email field is set to NotNull.
Solutions:-
1.Set email field in devise to allow null values.
2.so i had saved the email from twitter in my devise email field this is the code for twitter
def self.find_for_twitter_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
temp = Tempfile.new([auth["uid"], ".jpg"])
temp.binmode
temp.write(open(auth["info"]["image"]).read)
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],
image:temp
)
user.build_profile(name:user.name,image:temp)
user.profile.save
end
user
end

Related

Rails 4: User Authentication - NoMethodError

I've setup a moreless simple social user authentification on top of devise using Google, Linkedin, Dropbox and Github.The Dropbox authentication does not work, instead it gives that error on the callback URL(http://localhost:3000/users/auth/dropbox/callback):
NoMethodError in Users::OmniauthCallbacksController#dropbox
undefined method `first' for nil:NilClass
Issue: User Model (line 8)
My Code:
Callbacks Controller:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(env['omniauth.auth'], current_user)
if user.persisted?
sign_in user
flash[:notice] = t('devise.omniauth_callbacks.success', :kind => User::SOCIALS[params[:action].to_sym])
if user.sign_in_count == 1
redirect_to edit_user_registration_path
else
redirect_to root_path
end
else
session['devise.user_attributes'] = user.attributes
redirect_to new_user_registration_url
end
end
User::SOCIALS.each do |k, _|
alias_method k, :all
end
end
User Model:
# omniauth Gem
def self.from_omniauth(auth, current_user)
authorization = Authorization.where(:provider => auth.provider, :uid => auth.uid.to_s,
:token => auth.credentials.token,
:secret => auth.credentials.secret).first_or_initialize
authorization.profile_page = auth.info.urls.first.last unless authorization.persisted?
if authorization.user.blank?
user = current_user.nil? ? User.where('email = ?', auth['info']['email']).first : current_user
if user.blank?
user = User.new
user.skip_confirmation!
user.password = Devise.friendly_token[0, 20]
user.fetch_details(auth)
user.save
end
authorization.user = user
authorization.save
end
authorization.user
end
def fetch_details(auth)
self.email = auth.info.email
self.username = auth.info.name
self.avatar = URI.parse(auth.info.image)
end
I appreciate each help! Thanks in advance.
To answer your question directly:
The undefined method "first" for nil::NilClass is happening because you are attempting to call the method first on an empty, or nil object.
It's probably in your user model where you are attempting to find a User from a current_user.
if authorization.user.blank?
user = current_user.nil? ? User.where('email = ?', auth['info']['email']).first : current_user
#This will cause the error that you are describing if both the current_user is nil and there is no User whose email is auth['info']['email']
Now, There's a few things wrong with this. If they are attempting to log in to your application, then current_user at this stage should be unset.
You could try changing this to
user = User.where(email: auth['info']['email']).first_or_create
Which will create a new instance of User, if one does not exist with the email provided in the Authorization.
Then you can continue with
user.persisted?
which returns true for an existing user, and false for a new instance of User

Validation failed: Password can't be blank

I have an app, build on Rails 4.2 and tried to do twitter authentication, using RailsCasts #241 Simple OmniAuth.
But have this issue: Validation failed: Password can't be blank!
I searched answer everywhere, but did not find the solution!
user.rb
class User < ActiveRecord::Base
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
end
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def login
end
def create
#user = User.find_by_email(params[:email])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to root_path
else
redirect_to :back
end
end
def create_session
auth = request.env['omniauth.auth']
user = User.find_by_provider_and_uid(auth['provider'], auth['uid']) || User.create_with_omniauth(auth)
session[:user_id] = user.id
redirect_to root_url, notice: 'Signed in!'
end
def logout
reset_session
redirect_to root_path
end
end
routes.rb
get 'sessions/login'
get 'sessions/logout'
post 'sessions' => 'sessions#create'
get '/auth/:provider/callback' => 'sessions#create_session'
post '/auth/:provider/callback' => 'sessions#create_session'
get 'registration' => 'users#new', as: 'registration'
Solution
After editing user.rb model looks like:
class User < ActiveRecord::Base
has_secure_password
validates :password, length: { minimum: 6 }
require 'securerandom'
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.password = SecureRandom.urlsafe_base64
end
end
end
An alternative approach is to enter a random password on user creation. For example as shown in the omniauth-google-oauth2 readme. So you could alter your code to this:
require 'securerandom'
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.password = SecureRandom.urlsafe_base64
end
end
I think it's problem with
has_secure_password
try commenting it, and you can override the has_secure_password Or
has_secure_password(validations: false)
This is happening as it automatically adds
validates_presence_of :password_digest
So when specify allow_blank or allow_null it will not work

Cookie Overflow with Twitter sign in

I've got he kind of error when I tried to sign in with twitter on my website.
ActionDispatch::Cookies::CookieOverflow in Users::OmniauthCallbacksController#twitter
ActionDispatch::Cookies::CookieOverflow
I can't find a solution, this is the code I use.
omniauth_callbacks_controller.rb
def twitter
#user = User.find_for_provider_oauth(request.env["omniauth.auth"], current_user)
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "twitter") if is_navigational_format?
else
session["devise.twitter_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
devise.rb
require "omniauth-twitter"
config.omniauth :twitter, 'xxxxxxx', 'xxxxxxxxxxxk', :strategy_class => OmniAuth::Strategies::Twitter
user.rb
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.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
def self.find_for_provider_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],
invite_code:"42TXP"
)
end
user
Use: .except("extra")
session["devise.twitter_data"] = request.env["omniauth.auth"].except("extra")
It removes a big part from the cookie that is simply not needed.
It is what you put in the session in the else part of save
session["devise.twitter_data"] = request.env["omniauth.auth"]
It's probably too big for a cookie.
You should save that information somewhere else. Or don't keep it.
I recently had a similar problem after following the method in https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview, I hope this can help you.
In the OmniauthCallbacksController, instead of:
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"], current_user)
I use:
#user = User.find_for_facebook_oauth(request.env["omniauth.auth"].provider, request.env["omniauth.auth"].uid, request.env["omniauth.auth"].extra.raw_info.name, request.env["omniauth.auth"].info.email, current_user)
In the user model :
def self.find_for_facebook_oauth(provider, uid, name, email, signed_in_resource=nil)
user = User.where(:provider => provider, :uid => uid).first
unless user
user = User.create(:name => name,
:provider => provider,
:uid => uid,
:email => email,
:password => Devise.friendly_token[0,20]
)
end
return user
end
Also in OmniauthCallbacksController make sure you avoid using request.env["omniauth.auth"], use request.env["omniauth.auth"].uid instead.

I am using rails cast omniauth and i get this error

I am using Mongodb as database in rails and i got error when using /auth/linkedin/callback
NoMethodError in AuthenticationsController#create undefined method []' for nil:NilClass Rails.root: /home/prem/Music/heronhrm Application Trace | Framework Trace | Full Trace app/models/user.rb:57:in apply_omniauth' app/controllers/authentications_controller.rb:19:in `create'
Also when i remove self.email = omniauth['user_info']['email'] if email.blank? from usermodel then the validation errors arises in
users/sign_up Email can't be blank
I want to implement for twitter,linkdin and facebook.
my authentication.rb
class Authentication
include Mongoid::Document
belongs_to :user
field :user_id, :type => String
field :provider, :type => String
field :uid, :type => String
def self.find_by_provider_and_uid(provider, uid)
where(provider: provider, uid: uid).first
end
end
my user model is like this
def apply_omniauth(omniauth)
self.email = omniauth['user_info']['email'] if email.blank?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
My authentications controller is like this
class AuthenticationsController < ApplicationController
def index
#authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
#authentication = current_user.authentications.find(params[:id])
#authentication.destroy
flash[:notice] = "Successfully destroyed authentication."
redirect_to authentications_url
end
protected
# This is necessary since Rails 3.0.4
# See https://github.com/intridea/omniauth/issues/185
# and http://www.arailsdemo.com/posts/44
def handle_unverified_request
true
end
end
My registration controller is like this
class RegistrationsController < Devise::RegistrationsController
def create
super
session[:omniauth] = nil unless #user.new_record?
end
private
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
Inside your app/models/authentication.rb add this
def self.find_by_provider_and_uid(provider, uid)
where(provider: provider, uid: uid).first
end
Did you add this in your model? If not added then add this and then try
key :provider, String
key :uid, String

Devise Omniauth "encrypted_password may not be NULL" for new user

I am using Devise with Omniauth to have users sign into my app with Facebook. I used the Railscast tutorials to get it up and running.
If a user is already a member of my site authenticating through facebook works fine. The problem comes in when authenticating a new user with facebook. When it goes to create a new user for my User model I get the "users.encrypted_password may not be NULL" error. I can't figure out how to pass over the password to the User model from Facebook information.
This is what I have:
authentations_controller.rb
class AuthenticationsController < ApplicationController
def index
#authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
flash[:notice] = "Authentication successful."
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save
flash[:notice] = "Signed in successfully."
sign_in_and_redirect(:user, user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
user.rb
def apply_omniauth(omniauth)
self.email = omniauth['user_info']['email'] if email.blank?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
def password_required?
(authentications.empty? || !password.blank?) && super
end
Any help would be great, thanks in advance!
Add :password => Devise.friendly_token[0,20] when creating a new user from facebook omniauth.
I believe Devise is expecting something in the password field to create a User. Since there is no password when doing facebook oauth (not on your app side at least), you just need to create a dummy password as show above.
See this for more info:
https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview

Resources