In the process of switching from Devise to directly using Warden.
How can I go about implementing a "forgot password" functionality that comes out of the box with Devise?
Is there a gem that can add this onto Warden?
PS. The reason for not using Devise is because of some customization needed that makes hacking Devise to make it work not worthwhile.
I don't know if there is a gem, but doing it yourself doesn't take to long. Assuming that each account has an email attached to it. Have a forgot password button that links to a page where the user will input there username or whatever information that is unique to the user and once the user submits the form. Send a new password to there email.
Step-by-step:
Create a forgot password button on your login page to link to a new forgot password page.
Make the route, controller actions for the this new forgot password page.
Make a form on the forgot password page that takes in a unique piece of information about the user. ex. username. This form will be a post request to a action in your controller that will email a new password to the user who has this username for example.
This is what one of mine looked like:
def emailor
#user = User.find_by username: params[:user][:username]
random_password = Array.new(10).map { (65 + rand(58)).chr }.join
#user.password = random_password
if #user.save
UserMailer.reset_password_email(#user.email, random_password ).deliver
flash[:notice] = "Email has been sent";
redirect_to root_path
end
end
Make your mailer. This guide goes over how its done if you don't know. Mailers in Rails
I am using devise gem for authentication.
In my application admin will create the users, so I want the user's reset password link when admin creates users.
This is my action:-
def create
#user = User.new(user_params)
#user.password = '123123123'
#user.password_confirmation = '123123123'
if #user.save
#user.update_attributes(:confirmation_token => nil,:confirmed_at => Time.now,:reset_password_token => (0...16).map{(65+rand(26)).chr}.join,:reset_password_sent_at => Time.now)
UserMailer.user_link(#user).deliver
redirect_to users_path
else
render :action => "new"
end
end
This is my link to reset a user's password
But I am getting reset password token is invalid when I open the link and update the password.
If you are using devise why are you creating your own password reset token?
Devise has a feature for that.
http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Recoverable
In case you wonder this is what devise does when the user wants to reset his password:
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
self.save(validate: false)
self is a User object here
In your URL you then have to pass raw as reset_password_token parameter
You can generate a token with:
Devise.token_generator.generate(User, :reset_password_token)
Though this is just a useless string by itself. You need to attach it to the user if you actually want to use it in a link to reset passwords:
user.reset_password_token = hashed_token
user.reset_password_sent_at = Time.now.utc
Then send them an email with the link:
edit_password_url(#user, reset_password_token: #token)
You can use user.send_reset_password_instructions for that.
If you don't want it to send the instructions, just set and store the token you can call the private method in devise recoverable concern set_reset_password_token.
You can do this by doing something like user.send(:set_reset_password_token).
To get the url to reset the password using Devise I use this snippet of code:
user = User.find(id)
raw, enc = Devise.token_generator.generate(User, :reset_password_token)
user.update_columns(reset_password_token: enc, reset_password_sent_at: Time.current)
puts Rails.application.routes.url_helpers.edit_user_password_url(reset_password_token: raw, host: 'localhost:3000')
Expanding upon #Rails Fan's answer. The specific method that handles the password reset in Recoverable module is a protected method set_reset_password_token .
You can access it by the following code and it will return the token directly.
## your model.send(:set_reset_password_token)
user.send(:set_reset_password_token)
I would like to sign a user in if he attempts to sign up a second time ie. email and password in params is exactly the same.
I added this line to my RegistrationsController (if a duplicate email is detected)
resource = warden.authenticate!(auth_options)
however I can get it to successfully authenticate even though the correct email and password is provided in the params. am I missing something?
I'm on Rails 3.2 and Devise 2.2.7
#user = User.find_by_email(email)
if #user and #user.confirmed? and #user.valid_password?(password)
sign_in #user, :bypass => true
end
I'm building an API for a web app I'm developing, and the following code I'm trying to use for API authentication/login is returning false on the authorization.
In my API user controller I have:
def login
if params[:user]
# Find the user by email first
#user = User.where(email: params[:user][:email]).first
if !#user
respond_with nil
else
#auth = #user.authenticate(params[:user][:password])
if #auth
respond_with #user
else
respond_with #auth
end
end
end
end
It is always responding with #auth, which is false, even when valid email and passwords are being provided. It has no problem pulling the user info from my Mongo db.
I guess I'm just not clear on what .authenticate does. According to a railscast.com video I watched, it should compare that users password digest with the password entered. When a valid password is provided for the user, #auth is always false.
This method was actually working fine, the test data in the database wasn't what i thought it was..
I'm using code identical to my password_reset code for my email change code.
User wants change their email address so they type in their email address click a button and they're logged out.
An email is sent to them containing a link they click containing a code as id which is then matched up with the one stored in the db to confirm they are in fact the accounts owner. Any way when I click the I get the error shown below.
Problem is I'm getting this error:
ArgumentError in EmailsController#edit
comparison of String with ActiveSupport::TimeWithZone failed
Rails.root: /Users/greg/site
Application Trace | Framework Trace | Full Trace
app/controllers/emails_controller.rb:19:in `<'
app/controllers/emails_controller.rb:19:in `edit'
Request
Parameters:
{"id"=>"KdFTTeWuOGqpDm6F_iY7aw"}
Show session dump
Show env dump
Response
Headers:
None
Emails controller create:
def create
#user = User.find_by_email(params[:email_change][:email])
logout if logged_in?
#user.generate_and_store_email_change_token && UserMailer.email_change(#user).deliver if #user
flash[:success] = "Email sent with email reset instructions."
redirect_to root_url
end
Emails controller edit:
def edit
#user = User.find_by_email_change_token(params[:id])
if #user.nil?
flash[:error] = "The email change link you clicked has been used."
redirect_to root_url
elsif #user.email_change_sent_at < 2.hours.ago
flash[:error] = "Email change token has expired."
redirect_to email_change_url
end
end
User model:
def generate_and_store_email_change_token
self.email_change_token = SecureRandom.urlsafe_base64
self.email_change_sent_at = Time.zone.now
save!(:validate => false)
end
def remove_used_email_change_token
self.email_change_token = nil
save!(:validate => false)
end
This is strange because the exact same code works for my password reset. I tested again and I don't get the error the email version giving me.
Kind regards
Seems like email_change_sent_at type is a string. You should change it to datetime