Skip to content

Instantly share code, notes, and snippets.

@JaceTan
Last active December 15, 2015 20:09
Show Gist options
  • Save JaceTan/5316096 to your computer and use it in GitHub Desktop.
Save JaceTan/5316096 to your computer and use it in GitHub Desktop.
The ultimate forget_password pieces
<?php
// define whether we really want to send emails
define('EMAIL_ON', true);
<?php
echo $this->Form->create('User', array('action' => 'forget_password'));
echo $this->Form->inputs(array(
'legend' => __('Reset password'),
'User.email' => array('type' => 'email'),
));
echo $this->Form->end('Submit');
?>
<?php
//add this file to Lib/Email folder
App::uses('CakeEmail', 'Network/Email');
class PasswordEmail {
private $emailConfig = 'gmail';
private $sender = 'do-not-reply@example.com';
private $from;
private $to;
private $email;
public function __construct($to) {
$this->from = array('full_name' => 'Example', 'email' => $this->sender);
$this->to = $to;
$this->_prepareAddressFields();
}
protected function _prepareAddressFields() {
$fromEmail = $this->from['email'];
$fromFullName = $this->from['full_name'];
$toEmail = $this->to['email'];
$toFullName = $this->to['full_name'];
$email = new CakeEmail($this->emailConfig);
$email->from(array($fromEmail => $fromFullName));
$email->to(array($toEmail => $toFullName));
$email->sender($this->sender);
$email->replyTo($fromEmail, $fromFullName);
$this->email = $email;
return $email;
}
public function sendNewPassword($newPassword) {
$email = $this->email;
$email->subject('[Password Reset] Do NOT reply to this email');
if (EMAIL_ON) {
$result = $email->send("This is your new password $newPassword.");
} else {
$result = $email;
}
return $result;
}
public function sendToken($token) {
$email = $this->email;
$email->subject('[Password Reset] Do NOT reply to this email');
$baseURL = Router::url('/users/reset_password?token=' . $token, true);
if (EMAIL_ON) {
$result = $email->send("Please click this link to reset your password. $baseURL");
} else {
$result = $email;
}
return $result;
}
}
<?php
echo $this->Form->create('User', array(
'url' => Router::url(array(
'action' => 'reset_password',
'controller' => 'users',
'?' => array('token' => $token)
))
));
echo $this->Form->inputs(array(
'legend' => __('Please choose a new password'),
'User.id' => array('type' => 'hidden'),
'User.new_password' => array('type' => 'password'),
'User.confirm_new_password' => array('type' => 'password'),
));
echo $this->Form->end('Submit');
?>
<?php
App::uses('PasswordEmail', 'Lib/Email');
/**
*
* Checks the database to see if the email provided exists. If there is, return true.
*
* @param String $email Email Provided
* @return Boolean True if email exists, else return false
*/
public function checkEmailExists($email) {
$emailExists = $this->find('count', array(
'conditions' => array(
'email' => $email)
)
);
return ($emailExists === 1);
}
/**
*
* Makes a token using the email given and the current dateTime
*
* @param String $email Email Provided
* @return String $token The token
*/
public function createToken($email) {
$dateTime = date('Y-m-d H:i:s');
$plaintext = $email . $dateTime;
$token = Security::hash($plaintext, 'sha1', true);
return $token;
}
/**
*
* searches the users table for a token, given the email. If token is empty, create a new one and return it.
*
* @param String $email Email Provided
* @return Array $userData containing full_name, email and token
*/
public function findXORCreateToken($email){
$userData = $this->find('first', array(
'conditions' => array('User.email' => $email),
'fields' => array('User.id', 'User.full_name', 'User.token', 'User.email')
));
if ($userData['User']['token'] === null || empty($userData['User']['token'])) {
$token = $this->createToken($email);
$this->id = $userData['User']['id'];
$this->saveField('token', $token, array(
'callbacks' => false,
'validate' => false
));
$userData['User']['token'] = $token;
}
$userData = Hash::extract($userData, 'User');
return $userData;
}
/**
*
* Sends the user a token to reset password
*
* @param Array $userData Array containing full_name, email and token
* @return void
*/
public function sendToken($userData){
$recipient = array(
'full_name' => $userData['full_name'],
'email' => $userData['email'],
);
$email = new PasswordEmail($recipient);
$email->sendToken($userData['token']);
}
/**
*
* Resets the password
*
* @param Array $data Array containing id, new_password, confirm_new_password
* @return mixed return user data array or true if successful. false otherwise
* @throws CakeException when passwords don't match
*/
public function resetPassword($data){
//check if new pwd matches confirm pwd
if ($data['User']['new_password'] == $data['User']['confirm_new_password']) {
//save the pwd if successful
$data['User']['password'] = $data['User']['new_password'];
$data['User']['token'] = null;
$result = $this->save($data);
return $result;
} else {
throw new CakeException ('Your new passwords do not match.');
}
}
<?php
/**
* forget_password method
*
* @return void
*/
public function forget_password() {
if ($this->request->is('post')) {
$email = $this->request->data['User']['email'];
$emailExist = $this->User->checkEmailExists($email);
if ($emailExist === true) {
//concat datetime and email, and hash them to create token
$userData = $this->User->findXORCreateToken($email);
//send email with token
$this->User->sendToken($userData);
$this->Session->setFlash('The reset link has been sent to your email. Please check your email and click the link.');
$this->redirect(array('action' => 'login'));
} else {
$this->Session->setFlash('Did you enter a valid email address?');
}
}
}
/**
* reset_password method
*
* @return void
*/
public function reset_password() {
if(isset($_GET['token'])){
$userData = $this->User->findByToken($_GET['token']);
$validToken = $userData;
$this->set('token', $_GET['token']);
} else {
$validToken = false;
}
if (!$validToken) {
$this->Session->setFlash('Invalid link. Please Login.');
$this->redirect(array('action' => 'login'));
}
if ($this->request->is('get')) {
if ($validToken) {
$this->request->data = $userData;
}
} else if ($this->request->is('post') || $this->request->is('put')) {
try {
$result = $this->User->resetPassword($this->request->data);
$errorMessage = "Not successful";
} catch(CakeException $error) {
$result = false;
$errorMessage = $error->getMessage();
}
if ($result) {
$this->Session->setFlash('Password successfully changed');
$this->redirect(array('action' => 'login'));
} else {
$this->Session->setFlash($errorMessage);
}
}
}
@simkimsia
Copy link

Note the following assumptions in this code:

we are using an email config named gmail.
User models have the following fields, real or virtual: 'User.id', 'User.full_name', 'User.token', 'User.email'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment