I recently wrote a code for an email verification system using cakephp. Well, before you use it there are few things. This is written by a cake noob so may not be the best there is. I have not written it as a general stand alone piece of code. So its need modifications for use. And I am a sucker when it comes to commenting and formatting my code so please dont mind. My thanks to Edward and the CakePhP Google Groups. And finally cake rocks!!!
cheers
Database Table
A basic user table with the following columns
id - primary key and auto increment username email password [...] tokenhash* activate *
*columns with which we are concerned with
Controller code
<?php
class UsersController extends AppController {
var $name = 'Users';
var $helpers = array('Html', 'Form');
var $components = array('Email');
[...]
function register() {
if (!empty($this->data)) {
$this->User->data = $this->data;
$hash=sha1($this->data['User']['username'].rand(0,100));
//Create Token using form data and random number to ensure its unique and cannot be replicated
$this->User->data['User']['tokenhash']=$hash;
if ( $this->User->validates()) {
$this->User->save($this->data);
//Save all form data including the tokenhash
$ms='Click on the link below to complete registration ';
$ms.='www.sitename.com/users/verify/t:'.$hash.'/n:'.$this->data['User']['username'].'';
$ms=wordwrap($ms,70);
//create mail body
$this->Email->from = 'yourName <email>';
$this->Email->to=$this->data['User']['email'];
$this->Email->subject = 'Confirm Registration for Niwiki - reg.';
$this->Email->send($ms);
//send mail
$this->Session->setFlash('Please Check your email for validation Link');
$this->redirect('/users/login');
exit;
}
}
}
function verify(){
//check if the token is valid
if (!empty($this->passedArgs['n']) && !empty($this->passedArgs['t'])){
$name = $this->passedArgs['n'];
$tokenhash = $this->passedArgs['t'];
$results = $this->User->findByUsername($name);
//check if the user is already activated
if ($results['User']['activate']==0){
//check the token
if($results['User']['tokenhash']==$tokenhash)
{
//Set activate to 1
$results['User']['activate']=1;
//Save the data
$this->User->save($results);
$this->Session->setFlash('Your registration is complete');
$this->redirect('/users/login');
exit;
}
else{
$this->Session->setFlash('Your registration failed please try again');
$this->redirect('/users/register');
}
}
else {
$this->Session->setFlash('Token has alredy been used');
$this->redirect('/users/register');
}
}else
{
$this->Session->setFlash('Token corrupted. Please re-register');
$this->redirect('/users/register');
}
}else
{
$this->Session->setFlash('Token corrupted. Please re-register');
$this->redirect('/users/register');
}
}
function login() {
if (!empty($this->data)) {
if ( $this->User->validates()) {
$this->User->data = $this->data;
$results = $this->User->findByEmail($this->data['User']['email']);
//find the user based on the data from your login form
if ($results['User']['activate']==1)
//check if his activation is set to 1 by the verify function
{
// check if the passwords match
if ($results && $results['User']['password'] == md5($this->data['User']['password'])) {
$this->Session->write('User', $results['User']);
//logged in
} else {
$this->Session->setFlash('Invalid Username or Password please try again');
}
}
else {
$this->Session->setFlash('Your Email is not verified. Please verify and try again.');
}
}
}
}
[...]
}
?>
Model Code
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array(
[...]
);
?>
Views – register.ctp
<?php echo $form->create(null, array('url' => '/users/register'));?>
<h2> Fill out the form below to register </h2>
<?php echo $form->input('User.username') ?>
<?php echo $form->input('User.password',array('size' => '30',)) ?>
<?php echo $form->input('User.confirmpassword', array('size' => '30','type'=>'password',)) ?>
<?php echo $form->input('User.email') ?>
<?php echo $form->Submit('register') ?>
<?php echo $form->end(); ?>
Views – login.ctp
<?php echo $form->create(null, array('url'=>'/users/login'));?>
<h2>Please log in.</h2>
<?php echo $form->input('User.email') ?>
<?php echo $form->input('User.password') ?>
<?php echo $form->submit('login') ?>
<?php echo $form->error('User.email', $login_error) ?>
<?php echo $form->end(); ?>
May 8th, 2009 at 5:37 am
Hi,
Good job. I have done something 95% similar. The only thing I did extra was this:
When I generated the token, I appended the unix timestamp to the SHA digest. Then when I did the registration, I compared the timestamp portion to the current timestamp. With simple subtraction, like say less than 86400, means only allowing the code to be good for 24 hours(60*60*24), thus time expiring registration codes.
No worries about the 9 digit timestamp being in the clear since they can’t tamper with the value in the DB since we clearly match on that and then calculate the time difference between that value and the current time.
You could accomplish the same by adding another field in the User table or even checking with the creation date, it was just easier to tack it onto the token field since that was its sole purpose.
May 8th, 2009 at 9:44 am
thanks for that Tri Bui … I have a timestamp field for a/c creation.. but then i thought why trouble the poor user with a deadline… Maybe I implement it and set the deadline to 2 months or something like dat…
cheers
August 5th, 2009 at 8:57 pm
Glad my document helped you! For those concerned with expiring tokens I recommend a ‘expires’ column for each record. This way your not attached to a fixed date for all tickets. Since I use my tickets for much more than just passwords. Example – make password reset last a day, but maybe image sharing tokens last a week or longer. Purge the table of ‘expired’ tickets every time you validate – keeps your table clean too.
January 10th, 2010 at 3:27 pm
Thanx .. nice & simple, good if you dont have to rewrite things yourself all the time.
June 23rd, 2010 at 8:59 pm
thx a lot from germany….this helped me a lot…although i have a tiny problem
the token gets saved and the complete activation-link sent. but if i click the link i do not get redirected to my verify.ctp, i get to my login page. the view is in the correct file and exists as a function in my controller…also active is not updated to “1″ either…any ideas??
the weird thing: i commented all “redirect” commands out and cake is still redirecting me..why cant he go to activate????
thx a lot
July 13th, 2010 at 8:05 pm
Hello Thomas,
I am not sure what your problem is, but maybe the redirect page you have set up is viewable only when logged in. After token is verified, you need to login again to view pages.
Hope that makes sense.
Nikhil