Here is my user session controller create action that the login goes through
# POST /resource/sign_in
def create
resource = User.find_by_email(params[:user][:email])
# check for inactive
redirect_to(new_user_session_path, :notice => 'Invalid Email Address or Password.') and return if resource.try(:active) == false
# check to see if user is AD user
if ad_resource?(resource)
if !ActiveDirectory.new.authenticate!(params[:user][:email], params[:user][:password])
redirect_to new_user_session_path, :notice => 'Invalid Email Address or Password.'
return
end
else
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
end
set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource)
end
this line
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
How can that possible login anyone...I am not as familiar to devise and i am trying to login and i know there is an active user in the db but i cant log in and looking at the warden.authenticate line confuses me because it doensnt pass in the email and password...any help would be great to help me understand what is happening in the authentication
There is a warden initializer file its run as middleware that picks up the correct params when used with devise...
Have a look at the warden gem to understand how it works fully.
Personally i find devise gets in the way very quickly and you might be better understanding how it all works if you 'roll' your own authentication system.
Related
I have set confirmable in my devise user model but when I try to register email, which is already registered, I get error that email is registered or waiting for confirmation.
This is correct but for security reasons I want to always show something like "confirmation email has been sent to email address" so nobody can figure out which emails are already registered in the app.
Is this somewhere in devise config? Or do I have to manually modify registration controller to not throw any error if email exists in db?
The best way is way is to manually modify from registration controller (https://github.com/heartcombo/devise#configuring-controllers), you can rescue the error and changes the flash message.
I borrowed has_duplicate_email? from this post , checked devise source code and then modified create in registration controller and routes to use it. I hope it helps somebody
def has_duplicate_email?
return false unless resource.errors.has_key?(:email)
resource.errors.details[:email].any? do |hash|
hash[:error] == :taken
end
end
def create
super do |resource|
if has_duplicate_email?
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
redirect_to root_path and return
end
end
end
devise_for :users, :controllers => {:registrations => "users/registrations"}
Problem: How do I send the user an email after they sign up for the first time with Facebook? I'm using device and omniauth.
I have confirmation emails working for regular signup with devise. I need to send an email when the user gets added to my database for the first time after signing in with Facebook. Where in the code is this happening?
I tried adding a line of code sending the email in my omniauth_callbacks_controller.
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# omniauth_callbacks_controller
def facebook
#user = User.from_omniauth(request.env["omniauth.auth"])
facebook = "www.facebook.com"
if #user.persisted?
print "User persisted"
sign_in #user, :event => :authentication
set_flash_message(:notice,:success,:kind => "Facebook") if is_navigational_format?
# I SENT THE EMAIL HERE
else
session["device.facebook_data"] = request.env["omniauth.auth"]
redirect_to root_path
end
end
However, this just sends the user a confirmation email EVERY time they log in with Facebook, which is not what I want. I want to simply send the email the first time they log in.
The email should be sent in the registrations_controller. However, when users are signing up with Facebook, this controller is never used.
class RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_up(resource_name, resource)
# Tell the UserMailer to send a welcome email after save
UserMailer.welcome_email(current_user).deliver_later
return render :json => {:success => true}
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
expire_session_data_after_sign_in!
return render :json => {:success => true}
end
else
clean_up_passwords resource
invalid_signin_attempt
end
end
Would like to know the right way to send a confirmation to the user after signing up with Facebook.
Problem
It looks like your User.from_omniauth function behaves like a find_or_create call. That means that the controller has no knowledge of whether the user was just created or is being fetched from an existing identity in the database.
If the user is created as part of this from_omniauth call, then you should be able to just rely on the Devise :confirmable module. Otherwise, the user is created before you get back the OAuth credentials, so you need to handle it manually.
The code in the from_omniauth function likely looks something like this:
def self.from_omniauth(token)
user = User.find(token: token)
if user.nil?
user = User.create(token: token, ...)
# ...
end
# ...
end
There might be an intermediary Token, Identity, or other such class, but the logic should be the same.
Fix
There are two easy ways to fix this:
Include a created boolean as part of the from_omniauth return value, which the controller can then use to gate the confirmation email on.
Move the "create" part of the "find or create" logic out into the controller, so that the email can be sent as part of the "create" path.
Aside
Also, I'd suggest using the Devise resource.send_confirmation_instructions function and piggybacking your email off that. That way, all welcome emails share the same code and you're not maintaining a separate module just for Facebook/OAuth login.
I'm new in rails and I'm trying to integrate omniauth and the login with google.
Surfing on the web i found a way to do it, but i got this error:
undefined method `[]' for nil:NilClass
app/models/user.rb:22:in `find_for_open_id'
app/controllers/users/omniauth_callbacks_controller.rb:17:in `open_id'
I think that is because i don't access correctly the access token information.
here is the code:
Model users.rb
def self.find_for_open_id(access_token, signed_in_resource=nil)
data = access_token['user_info']
if user = User.find_by_email(data["email"])
user
else # Create a user with a stub password.
User.create!(:email => data["email"], :password => Devise.friendly_token[0,20])
end
end
omniauth_callbacks_controller.rb
def open_id
# You need to implement the method below in your model
#user = User.find_for_open_id(env["omniauth.auth"], current_user)
if #user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
sign_in_and_redirect #user, :event => :authentication
else
session["devise.open:id_data"] = env["openid.ext1"]
redirect_to new_user_registration_url
end
end
devise.rb
config.omniauth :open_id, :store => OpenID::Store::Filesystem.new("/tmp"), :name => 'open_id', :identifier => 'https://www.google.com/accounts/o8/id'
I don't know what else i can show you.
For me the problem is here:
data = access_token['user_info']
access_token['user_info'] returns null.
Am I doing something wrong? Is there other way to access the 'user_info' and then the e-mail?
Thanks in advance. Hope i'm clear.
You should just be using data = access_token['info'] instead of data = access_token['user_info']
From there you have access to data['email], data['first_name'], and data['last_name'].
I currently use Devise for user registration/authentication in a Rails project.
When a user wants to cancel their account, the user object is soft deleted in a way like the following.
How to "soft delete" user with Devise
My implmenetation has a small difference this way.
User model has an attribute 'deleted_flag'.
And, soft_delete method executes "update_attribtue(:deleted_flag, true)"
But, I have to implment sign_in action.
In my implmenetation is the following.
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
if resource.deleted_flag
p "deleted account : " + resource.deleted_flag.to_s
sign_out(resource)
render :controller => :users, :action => :index
else
if is_navigational_format?
if resource.sign_in_count == 1
set_flash_message(:notice, :signed_in_first_time)
else
set_flash_message(:notice, :signed_in)
end
end
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
end
end
end
I think this code has strange points.
If deleted user tries to sing in,
the system permit logging and make log out immediately.
And, the system cann't display flash[:alert] message...
I want to know two points.
How do I implement to prohibit deleted users to login?
How do I implement to display flash[:alert] when deleted user tries to login?
To stop a user that has been 'soft deleted', the best way is to overwrite the find_for_authentication class method on the user model. Such as:
Class User < ActiveRecord::Base
def self.find_for_authentication(conditions)
super(conditions.merge(:deleted_flag => false))
end
This will generate a invalid email or password flash message by devise (because it cannot find the user to authenticate)
As far as your second question though, you'll need some for of method in your controller to add a particular flash message. However, in my opinion you should treat users that are 'soft' deleted the same as if they didn't exist in the database at all. Thus if they tried to log in, they should just get an valid email or password message.
See my solution here: https://stackoverflow.com/a/24365051/556388
Basically you need to override the active_for_authentication? method on the devise model (User).
I haven't tried anything like that but it seems if you want to catch the user before authentication you'll either have to write a Devise authentication strategy or a before_filter to be run before authenticate_user!. Something like:
before_filter :no_deleted_users
def no_deleted_users
if User.find(params[:email]).deleted?
redirect_to root_path, :flash => { :error => "Your user was deleted. You cannot log in." }
end
end
Although it might be more complex to get the user than that. I haven't played with Devise pre-authentication.
The modern and correct answer is this:
class User < ApplicationRecord
def active_for_authentication?
super && !discarded? # or whatever...
end
end
See the documentation here.
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