onrails.org home

Forgot Password?

I did it again…forgot my password. Now if everyone could offer an openid login like Highrise. This time it happened on myconfplan, while I was organizing my schedule for next weeks RailsConf. As I didn’t find a link to reset the password on myconfplan, I send an email to their support. Dr Nic replied promptly and said he didn’t implement this yet on this wedsite, but he could manually reset the password. Well, recently I implemented that feature for MySpyder.net (one of our forthcoming web applications). So I send him some code snippets. Not sure if Dr Nic will use them, but maybe some of our blog readers may be interested, so here we go.

They are several ways to implement a "Forgot Password? This time we choose to send out a “reset password” link that is valid for 24 hours. This link lets the user login, bypassing the standard login, and showing the change password screen.

First lets add a migration.

class ForgotPassword < ActiveRecord::Migration
def self.up
add_column :users, :reset_password_code, :string
add_column :users, :reset_password_code_until, :datetime
end

def self.down remove_column :users, :reset_password_code remove_column :users, :reset_password_code_until end

end

“Forgot password” form.

Then add a “forgot password” form, allowing the user to submit the email to which the “reset password” link will be emailed. When the form is submitted, the controller creates a ‘reset password code’ that is valid for one day, and sends an email to the user.

def forgot_password user = User.find_by_email(params[:email]) if (user) user.reset_password_code_until = 1.day.from_now user.reset_password_code = Digest::SHA1.hexdigest( “#{user.email}#{Time.now.to_s.split(//).sort_by {rand}.join}” ) user.save! UserNotifier.deliver_forgot_password(user) render :xml => “Reset Password link emailed to #{user.email}.” else render :xml => “User not found: #{params[:email]}” end end

Send email with the ‘reset password’ link.

When the user receives the “reset password” email and clicks the link to reset the password, the reset_password method is invoked on the controller. The user associated with the “reset_code” is found, and if the the reset_code is not yet expired the user is automatically logged-in and redirected to the account page where he can change his password. Note that by adding an expiration attribute for the code, we don’t need to run a cleanup batch process to invalidate these codes. Not in the following code we redirect to a “.swf” file. This was an early experiment where the user interface of the application was written in Flex. We are currently rewriting it to use a more traditional html and css approach.

def reset_password user = User.find_by_reset_password_code(params[:reset_code]) self.current_user = user if user && user.reset_password_code_until && Time.now < user.reset_password_code_until redirect_to logged_in? ? “/MySpyder.swf?a=account” : “/MySpyder.swf?a=login” end

The email is simply send using the following ActionMailer.

class UserNotifier < ActionMailer::Base
def forgot_password(user)
setup_email(user)
@subject += ‘MySpyder.net – Reset Password’
@body[:url] = “http://myspyder.net/reset_password/#{user.reset_password_code}”
end

protected def setup_email(user) @recipients = “#{user.email}” from = "adminmyspyder.net" @subject = "[myspyder.net] " @sent_on = Time.now @body[:user] = user end

end

And the view for the UserNotifier is the following

<%= @user.email %>,

You can reset your password by using the following link <%= @url %>

Thank your for using MySpyder.net

Fork me on GitHub