Devise, OmniAuth & Facebook - How to let user edit password? - ruby-on-rails

I'm hoping that someone else has a good solution for this issue. We let our users register using facebook (by liking an app), and at the same time they enter our database as users on our site.
Upon successful registration it seems like Devise/OmniAuth is creating a random password(?). How can I let users edit their profile, which (and should) by default in Devise requires that they enter their current password?

I had the exact same issue so I hope my solution will be helpful.
Based off the details in your question I am assuming you following the OmniAuth guide in the devise wiki: https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
In the following method:
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else # Create a user with a stub password.
User.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
I changed the logic in the else block because it was creating a new user in the database right away and hashing a password for them:
else # Create new user
user =User.new
user
end
Instead I just made a new user so that after getting the facebook info I direct them to the sign up page where I have their info populated in the form fields where they can edit and create a password.
You will just need to make sure to update your
def self.new_with_session(params, session)
to add all the relevant facebook information you grabbed for a new user and assign it to the new user object so all those fields are populated with their information in the sign up page. So that after they finish typing their password and adding or changing any info and click submit it create the new user. Hopefully you find this helpful.
This was a mash of ideas for the wiki page on Devise and the railscast omniauth tutorial: http://railscasts.com/episodes/235-omniauth-part-1

Had a similar problem, but in regards to updating a user profile without password confirmation. Posting a link to it - hope it helps:
stackoverflow - Allowing users to edit accounts without saving passwords in devise

Related

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.

Rails How to Login a Guest User with Devise

So I have this app that I'm making where users have profiles after they signup and input their information.
At the moment I'm trying to add a feature that allows for new unregistered users to go to a profile to see what the app is like before they need to sign up (I'm planning on putting a "try it for free" button on the home_controller#index action. For authentication, I'm using the Devise gem.
Currently, I've watched the Railscast (393) on this, but I can't figure out (after several hours of trying) how to implement guest users and log them in using Devise.
I've also read about 4 different solutions on SO, and have decided to stick to this one (how to create a guest user in Rails 3 + Devise):
class ApplicationController < ActionController::Base
def current_user
super || guest_user
end
private
def guest_user
User.find(session[:guest_user_id].nil? ? session[:guest_user_id] = create_guest_user.id : session[:guest_user_id])
end
def create_guest_user
u = User.create(:name => "guest", :email => "guest_#{Time.now.to_i}#{rand(99)}#example.com")
u.save(:validate => false)
u
end
...
end
I have this in my application_controller.rb and don't understand how I would use these functions in the home_controller#index to create a guest user and log into the profile when the "Try it" button is clicked.
I've tried manually creating a user, saving it without authentication and then using Devise's sign_in method on link like so: Try it! and also tried
Try it!
I tried this, but the profile throws some validation messages saying I need to log in to view it. I've also tried removing before_filter authenticate! on the profiles_controller but I can't seem to get this to work at all.
Would anyone know how to create a user on the button click, and auto sign them into a guest profile? Thanks a lot for any help, I'm completely lost here.
I think you have a misunderstanding on what a guest user is. You say that you want guest users to auto sign in, and that is wrong. Guest users can't sign in, because... Well, because they are guests. You want to create them, but not sign them in.
This is why you want these helper methods in your ApplicationController, because when you try to get the current_user, if that doesn't exist (nil), you will have a fallback (that is why you use the || operator), that will assign a guest_user as a current_user.
So, forget about using sign_in links for guest users and you should be fine.

Create new user session without password in authlogic

I am using authlogic and omniauth for authentication in my Rails 3.0.10 app.
However when I get the callback from the omniauth provider, I am not able to create a new user session so as to sign in the user.
In other responses (and even in the authlogic docs), it says that using UserSession.create(#user, true) should be able to create and persist a new session.
However, this does not work for me. It only works if the #user has a password in the database (by inserting a password directly in the db).
But there is no password when using third-party authentication providers, hence I cannot sign in users.
Any ideas how to sign in a user without a password in authlogic?
Thanks.
You can do something like this in your User model:
acts_as_authentic do |config|
external = Proc.new { |r| r.externally_authenticated? }
config.merge_validates_confirmation_of_password_field_options(:unless => external)
config.merge_validates_length_of_password_confirmation_field_options(:unless => external)
config.merge_validates_length_of_password_field_options(:unless => external)
end
externally_authenticated? is just a method on the user that checks what is providing the user information, and if it's one of the omniauth providers, returns true.

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)

rails Devise - How to redirect to a Getting Started Link - After validating an email

I'm using Devise with my rails 3 app. The app requires users to validate their email before continuing.
How can I redirect users to a specific url like /gettingstarted after they successfully validate their email address via the email confirmation msg they receive?
Thanks
When a user clicks on the confirm link they are taken to a confirm page which checks the confirmation token and if it's valid automatically logs them into the application. You could overwrite the after_sign_in_path_for method in your ApplicationController (as shown on the Devise wiki) and then redirect them to your getting started page the first time a user logs in.
def after_sign_in_path_for(resource_or_scope)
if resource_or_scope.is_a?(User) && first login
getting_started_path
else
super
end
end
For "first login" you could test if the confirmed_at timestamp is within a couple minutes of now, if your also using the trackable module in devise you can check if the sign_in_count is 1 or you could create your own field in the user model that tracks this information.
I'm checking devise source code at https://github.com/plataformatec/devise/blob/master/app/controllers/devise/confirmations_controller.rb
and seems that we have a callback to do it "after_confirmation_path_for"but I couldn't get it working without rewrite Devise::ConfirmationController
I hope that helps and if somebody get it working just defining after_confirmation_path_for just let us know.
I'm using the last_sign_in_at field from the 'trackable' model to achieve this. I've got the following code in my root action:
if current_user.last_sign_in_at.nil? then
redirect_to :controller => :users, :action => :welcome
end
http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Trackable
Seems to work reasonably well.
inside the 'after_sign_in_path_for' the current_user.last_sign_in_at.nil? will not work since it is alerady after the first sign-in. However this will work
if current_user.sign_in_count == 1
# do 1 thing
else
# do another thing
end

Resources