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
Related
I'm developing an Ruby on Rails webapp and I'm trying to use LDAP authentication to authenticate my users, I have the connection set up and working to the LDAP, but now I can't find any examples or documentation online on how to write code to authenticate users against my LDAP on Ruby on Rails
I'm using: Ruby v2.2 and Rails v5.0.3 and the gem I'm using to connect to ldap is gem 'net-ldap', '~> 0.16.0'
This is my login form at the moment, authenticating with a sqlserver DB, but I want it to authenticate against my LDAP DB :
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_NumeroEmpregado(params[:NumeroEmpregado])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to '/'
else
flash[:error] = "Erro! \nNĂºmero de Empregado e/ou password incorrecto(a)"
redirect_to '/login'
end
end
def destroy
session[:user_id] = nil
redirect_to '/index/new'
end
end
users_controller.rb
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to '/'
else
flash[:error] = "Erro! \nNenhum dos campos pode ser deixado em branco"
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:NumeroEmpregado, :nome, :password, :password_confirmation)
end
end
How can I reformulate this code into authenticating with my LDAP DB?
You could create a service that handles that process:
app/services/authenticate_user.rb
class AuthenticateUser
def initialize(user, password)
#user = user
#password = password
end
def call
user_is_valid?
end
private
def user_is_valid?
ldap = Net::LDAP.new
ldap.host = your_server_ip_address
ldap.port = 389
ldap.auth(#user, #password)
ldap.bind
end
end
Then use it in your controller:
class SessionsController < ApplicationController
def new
end
def create
username = params[:NumeroEmpregado]
password = params[:password]
name = "Some Name" # Change "Some Name" to set the correct name
if AuthenticateUser.new(username, password).call
user = User.create_with(nome: name).find_or_create_by(NumeroEmpregado: username)
session[:user_id] = user.id
redirect_to '/'
else
flash[:error] = "Erro! \nNĂºmero de Empregado e/ou password incorrecto(a)"
redirect_to '/login'
end
end
def destroy
session[:user_id] = nil
redirect_to '/index/new'
end
end
AuthenticateUser.new(user, password).call will return true when valid user and password are provided, and will return false otherwise.
This is a basic example covering only the LDAP authentication, you will need to adapt it for your specific needs, including exception handling.
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
I'm using Omniauth to try to signup/login users on my web app.
This is happening inside my AuthenticationsController#Create method:
Authentications Controller:
class AuthenticationsController < InheritedResources::Base
def create
omniauth = request.env['omniauth.auth']
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
flash[:success] = "Signed in successfully"
sign_in_and_redirect(authentication.user)
elsif current_user
token = omniauth['credentials'].token
secret = omniauth['credentials'].secret
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => token, :secret => token_secret)
flash[:success] = "Authentication successful"
redirect_to authentications_url
else
user = User.new
user.apply_omniauth(omniauth)
if user.save!
flash[:success] = "Account created"
sign_in(authentication.user)
else
session[:omniauth] = omniauth.except('extra')
redirect_to '/signup'
end
end
end
I originally had sign_in(:user, authentication.user) but it gave me argument errors, so I changed it to just had sign_in(authentication.user) in the above code. However, now I'm getting a NoMethodError - undefined method 'user' for nil:NilClass.
Line 23 sign_in(authentication.user) is failing because you are in the else branch of if authentication condition (so authentication is nil).
What you probably meant to do is call sign_in(user) for user you just created a few lines above.
First you test if authentication is defined, and then in the else block, which will only trigger if it is not defined, you go and reference authentication.user for some reason. This could not possibly work.
That condition seems to apply if you haven't authenticated, and aren't logged in. Why would you be engaging the sign_in function under those circumstances?
If your using a set list of providers in your db make sure
> :provider => omniauth['provider']
Matches up with the provider in the db, for instance. If you have facebook_Oauth2 in your db and the provider response is facebook authenthication will fail.
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
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