How to Change or Reset your Password with RESTful_authentication
I have used the acts_as_authenticated plugin for quite some time. It works well, and there’s lots of documentation. I’ve been happy with it. Mostly. I say mostly because it has always bothered me that acts_as_authenticated generates unRESTful code, which is tainting my perfectly RESTful application.
So, I recently evaluated the restful_authentication plugin. The plugin works great, but it is lacking the same breadth of documentation. I needed to add the ability handle a lost password and to change a password. Here’s what I did:
1. Create a new Passwords controller.
The new/create actions equate to a lost password (user wants to get a new password). The edit/update actions equate to changing a password (user wants to update their password).
When a user forgets their password and requests a new one, both their login and email will be required. If this login and email pair exists, then a new, random password will be generated and emailed.
When a user desires a new password, they are required to enter their old password in addition to typing and confirming a new password. The old password is required for security sake.
class PasswordsController < ApplicationController
before_filter :login_from_cookie
before_filter :login_required, :except => [:create]
# Don't write passwords as plain text to the log files
filter_parameter_logging :old_password, :password, :password_confirmation
# GETs should be safe
verify :method => :post, :only => [:create], :redirect_to => { :controller => :site }
verify :method => :put, :only => [:update], :redirect_to => { :controller => :site }
# POST /passwords
# Forgot password
def create
respond_to do |format|
if user = User.find_by_email_and_login(params[:email], params[:login])
@new_password = random_password
user.password = user.password_confirmation = @new_password
user.save_without_validation
UserNotifier.deliver_new_password(user, @new_password)
format.html {
flash[:notice] = "We sent a new password to #{params[:email]}"
redirect_to signin_path
}
else
flash[:notice] = "We can't find that account. Try again."
format.html { render :action => "new" }
end
end
end
# GET /users/1/password/edit
# Changing password
def edit
@user = current_user
end
# PUT /users/1/password
# Changing password
def update
@user = current_user
old_password = params[:old_password]
@user.attributes = params[:user]
respond_to do |format|
if @user.authenticated?(old_password) && @user.save
format.html { redirect_to user_path(@user) }
else
format.html { render :action => 'edit' }
end
end
end
protected
def random_password( len = 20 )
chars = (("a".."z").to_a + ("1".."9").to_a )- %w(i o 0 1 l 0)
newpass = Array.new(len, '').collect{chars[rand(chars.size)]}.join
end
end
2. Here is the change password view (edit.html.erb):
<div class="form_container">
<%= error_messages_for :user %>
<% form_for(:user, :url => user_password_path(@user), :html => { :method => :put }) do |f| %>
<fieldset>
<div class="form_row">
<label for="password">Old password</label>
<%= password_field_tag :old_password %></div>
<div class="form_row">
<label for="password">New password</label>
<%= f.password_field :password %></div>
<div class="form_row">
<label for="password_confirmation">Retype the new password</label>
<%= f.password_field :password_confirmation %></div>
<div class="submit_row">
<%= f.submit "Update", :class => "submit" %> or <%= link_to 'Cancel', home_path %></div>
</fieldset>
<% end %></div>
3. Here is the new password view (new.html.erb):
<div class="form_container">
<% form_for(:password, :url => passwords_path) do |f| %>
<fieldset>
<div class="form_row">
<label for="login">Username</label>
<%= text_field_tag 'login' %></div>
<div class="form_row">
<label for="email">Email</label>
<%= text_field_tag 'email' %></div>
<div class="submit_row">
<%= submit_tag 'Send Password', :class => "button-to" %></div>
</fieldset>
<% end %></div>
4. Here’s the user notifier (emailer) code:
You’ll need a user_notifier.rb file and a view template (app/views/user_notifier/new_password.html.erb) which will just contain the text of the email you want to send.
app/models/user_notifier.rb:
class UserNotifier < ActionMailer::Base
def new_password(user, new_password)
setup_email(user)
@subject += 'Your new password'
@body[:new_password] = new_password
end
protected
def setup_email(user)
@recipients = "#{user.email}"
@from = "Support" "<support@yoursite.com>"
@subject = ""
@sent_on = Time.now
@body[:user] = user
end
end
app/views/user_notifier/new_password.html.erb:
We’re sorry to hear your lost your password. But, there’s no need to worry, because we’ve created a new, temporary password for you.
Your new password is: <%=h @new_password %>
You can change this password to something more memorable once you log into your account.
5. Here’s the modified routes.rb file:
map.resources :passwords map.resources :users, :has_one => [:password]
21 Comments to How to Change or Reset your Password with RESTful_authentication
Leave a Reply
About Justin
Search
Recent Posts
- A Simple Formula for Evaluating Risk
- Not Having a Plan B Makes Plan A More Successful
- Easy Rails API Authentication Using restful-authentication
- God Init.d Script for CentOS
- Private, Authenticated RSS Feeds in Rails
- A Private Web Beta in Seconds with Prefinery
- Backup Your WordPress Blog to Amazon S3 using Ruby
- How to Set an Expires Header in Apache



the code look so compelling that i am actually trying to integrate it into my project.
however,
i can’t piece the things together.for example
i.e. if the user has forgotten their password. what link (link_to) do i give them from where? i.e signup.
it would be great if you could provide a little more such information so that it is more understandable.
also i don’t understand – map.connect ”, :controller => ‘site’
what is site in this case? i.e i dont have controller site.
Thanks for the help—–
If a user is logged in and they want to change their password, link to edit_user_path(current_user). If a user is not logged in and has forgotten their password, link to the forgot password view by using new_password_path().
The route to the site controller has nothing to do with the password code. It was a copy/paste error and I’ve removed it from this article.
Thanks for the feedback!
Thanks for the reply.
I have have plugged in the code as above and I am getting a
uninitialized constant PasswordsController::UserNotifier
at the line: UserNotifier.deliver_new_password(user…….
Excuse the nuby but what am i missing?
thanks for the help.
Justin,
so i got it working….it well coded and there are lots of new expressions that i will be using from now on – thanks.
On another note, there really is a ton of code missing from your example (an a few errors) which makes it very difficult if not impossible for us newbies to get to grips with it. On the upside – i learned a lot.
Still maybe you could find the time add the missing code for others – i will be documenting what i did on my blog for safe keeping.
thanks again for a great snippet.
Hey,
better use:
find_in_state :first, :active, :conditions => ['LOWER(login) = :login && LOWER(email) = :login', {:login => login.downcase, :email => email.downcase}]
First of all, thanks for the helpful demonstration of REST@work. I noticed one little error in one of your comments. It should be:
“If a user is logged in and they want to change their password, link to edit_user_password_path(current_user)”
Thanks, this is exactly what I needed… but can you please post your UserNotifier code?
As requested, I’ve updated the post with the UserNotifier code. I hope this helps.
I found your site on technorati and read a few of your other posts. Keep up the good work. I just added your RSS feed to my Google News Reader. Looking forward to reading more from you down the road!
I’m pretty new to rails and I added this great bit of code to a restful_authentication project. My problem is with the validation. With your code, if you have the wrong old_password entered and everything else empty or wrong you only get a message about the old_password. If I enter the correct old_password and wrong other stuff, you still get the old_password is incorrect message.
Since the user model is used to save the data to the db, I was trying to let the model handle the validation of all the fields but haven’t been able to figure it out. Do you have a suggestion on how to refactor the password change and validation code into the user model? I keep getting “nil where you didn’t expect it errors” since I still don’t fully understand all the scoping of the rails system.
Thanks,
Eric
Thanks Man! Worked great and is much more lightweight than a lot of what I’ve seen out there re: restful authentication plugin.
This is a good post, but I think there is a big fundamental mistake here. What’s to keep me from resetting random user’s passwords? Let’s say I know someone’s login is “john”, but that is not my login. I don’t like John, so I will go and reset his password every day.
@Ben The PasswordsController update method will only change the password for “current_user” which, in RESTful_authentication” is set once the user authenticates first. So, you could only change John’s password if you could authenticate as him.
I still don’t get it. I think Ben is talking to the PasswordsController Create method. As in, if I know your username and email, I can be resetting your password as many times as I want, and I could be doing that everyday. Isn’t it so?
Hi, thanks for this elegant and RESTful solution to this problem. I have one question however: it seems to me that the generation of a new password and the sending of the notification email would be better located in a Model method than in the controller.
However, since the User model is generated by restful_authentication, I thought that might be your reason for putting this logic in the controller (keeping the restful_authentication stuff “pure”).
In my application, I’ve put it in the User model, but was just wondering if there was something I’m missing?
Generate random password in Rails.
http://www.railsgeek.com/2009/1/6/generate-random-password-in-rails
I am using Restful_authentication plugin for one of my projects. As part of my user creation workflow, system should to generate a random password for the new user.
Hi,
I follow your post but somehow in my case this
@user.authenticated?(old_password)
retur false.
Any idea what’s wrong?
Thanks
thanks!!! really helpful!!
So I tried to make it so that when the user logs in with the @new_password that it would go to the edit password page so they have to change it. This is what I put it in but it doesn’t work…
This is in the create method of the Sessions controller.
if user.password == @new_password
redirect_to edit_user_password_path(user)
else
redirect_back_or_default(‘/’)
flash[:notice] = “You are now logged in.”
end
Any ideas?
Authentication on whether the passwords are of correct length are bypassed somehow. Any ideas?
Thanks a lot, worked great.