Build a forgot password system in fuelPHP

I built myself a forgot password system in fuelPHP, and have needed it a few times since, so I thought i would write it here.

I needed something like this:

User visits /forgot, enters their email address, we check that it exists, if so, generate a unique string and email them. The link should expire after 2 hours.

User clicks link and visits /recover/(unique string), we check string, along with the users email address against the db and verify they exist. We also check to see if forgot_at is within our 2 hour expiry window.

If this all passes, we show the user a form where they can enter a new password.

Update the password field and redirect user to /recovered.

Done.

So, here’s the methods, i won’t add views as they are essentially just forms and success/fail messages.

The methods are part of my Home controller.

The Controller Methods

action_forgot()
action_recover()
action_recovered()

/forgot – The Forgot Method

[code]
public function action_forgot()
{
if ( $_POST )
{
$email = filter_var(Input::post(’email’), FILTER_SANITIZE_EMAIL);
$u = Model_User::query()->where(’email’, $email)->get();
$id = reset($u)[‘id’];
if ( isset($u) && !empty($u) )
{
$user = Model_User::find($id);

$rand = Str::random(‘unique’);
$link = Uri::base(false) .’recover/’ .urlencode($email) .’/’ .$rand;
$user->forgot_rand = $rand;
$user->forgot_at = date("U");
$user->save();

$data[‘u’] = $user;
$data[‘link’] = $link;
$email = \Email::forge();
$email->to($user->email, $user->username);
$email->subject(‘Password Recovery ‘);
$email->html_body(\View::forge(’email/recoverpassword’, $data));
$email->from(‘automate@domain-changed.co.uk’, ‘Stuarts System’);
try
{
$email->send();
// its been sent…
Session::set_flash(‘success’, ‘Email sent to ‘ .$user->email .’. Password recovery links expire in 2 hours.’);
$this->template->title = ‘Recovering Password’;
$this->template->content = View::forge(‘home/recovering’);
}
catch(\EmailSendingFailedException $e)
{
die(‘Failed sending email.’);
}
catch(\EmailValidationFailedException $e)
{
die(‘Email address failed to validate.’);
}
}
else // email not found
{
Session::set_flash(‘error’, ‘Can\’t find that email address.’);
Response::redirect(‘/forgot’);
}
}
else
{
$this->template->title = ‘Forgot password’;
$this->template->content = View::forge(‘home/forgot’);
}

}
[/code]

/recover – The Recover Method

[code]
public function action_recover($email, $forgot_rand)
{
$expire = date("U", strtotime(‘-2 hours’));
$u = Model_User::query()->where(‘forgot_rand’, $forgot_rand)->where(’email’, urldecode($email))->where(‘forgot_at’, ‘>’, $expire)->get();
if ( $u ) $u = reset($u);
if ( !isset($u->id) )
{
Session::set_flash(‘error’, ‘Invalid request or password reset has expired.’);
Response::redirect(‘/forgot’);
}
else
{
if ( $_POST && !empty($_POST[‘password’]) )
{
$password = Input::post(‘password’);
$val = Model_User::validatePassword(‘password’);
if ($val->run())
{
$u->password = Auth::hash_password((string) Input::post(‘password’));
$u->forgot_rand = ”;
$u->forgot_at = ”;
$u->save();
Session::set_flash(‘success’, ‘Your new password has been set.’);
Response::redirect(‘/recovered’);
}
else
{
die(‘password fail’);
}
}
else
{
$this->template->title = ‘Recover Password’;
$this->template->content = View::forge(‘home/recover’);
}
}
}
[/code]

/recovered – The Recovered Method

[code]
public function action_recovered()
{
$this->template->title = ‘Recovered’;
$this->template->content = View::forge(‘home/recovered’);
}
[/code]

Define the Routes

[code]
‘forgot’ => array(‘home/forgot’, ‘name’ => ‘forgot’),
‘recover/(:any)/(:any)’ => array(‘home/recover/$1/$2’, ‘name’ => ‘recover’),
‘recovered’ => array(‘home/recovered’, ‘name’ => ‘recovered’),
[/code]




2 Comments

I can’t seem to figure out how to implement this. Can you give some hint about what to write in view ’email/recoverpassword’ ? Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *