Very Simple User Authentication with CakePHP

DISCLAIMER: I created this super simple user authentication post for CakePHP version 1.2.7. The latest versions of CakePHP (v2+) have been heavily enhanced. As a side effect, the code in this post will no longer work out-of-the-box. However, I’ll continue to leave this post up as a learning tool. Happy coding!

I’m working with my Software Engineering II class to build an online survey for United Way. I was left in charge to create a simple way to login and authenticate.

The following implementation uses sessions exclusively. I built it so you MUST log-in before accessing any part of the site/survey. You could easily build off this, or even switch to Cake’s built-in ACL and Auth features.

Preliminaries

The database structure presented is the same as in my previous post about a user account class. Here it is again however:

The Login Form View

The login form is extremely simple using Cake’s built in form helper. Here’s how we do it:

<?php
// Pass the model name into the Create function, also pass where the data will be sent
echo $form->create('User', array( 'controller' => 'users', 'action' => 'login' ) );

// Cake automatically knows, based on the input, to create input fields for these two. Cool eh?
echo $form->input('username');
echo $form->input('password');

// Create the submit button
echo $form->end('Login');
?>

That’s much less code, and way clearer once you get used to it. I placed this file in /app/views/users/login_form.ctp

The User Model

<?php
class User extends AppModel {
	function validateLogin($data)
	{
		// Search our database where the 'username' field is equal to our form input.
		// Same with the password (this example uses PLAIN TEXT passwords, you should encrypt yours!)
		// The second parameter tells us which fields to return from the database
		// Here is the corresponding query:
		// "SELECT id, username FROM users WHERE username = 'xxx' AND password = 'yyy'"
		$user = $this->find(array('username' => $data['username'], 'password' => $data['password']), array('id', 'username'));

		if( empty($user) == false )
		{
			return $user;
		}

		return false;
	}
}
?>

This code should go in /app/models/user.php

The Users Controller

<?php
class UsersController extends AppController {
	var $name = 'Users';
	var $helpers = array('Form');

	// Placeholder for login_form, required by CakePHP to see the login_form view
	function login_form() { }

	function login() {
		// Check if they went here after submitting the form
		// Note that all our form data is preceded by the model name ['User']
		if(empty($this->data['User']['username']) == false)
		{
			// Here we validate the user by calling that method from the User model
			if(($user = $this->User->validateLogin($this->data['User'])) != false)
			{
				// Write some Session variables and redirect to our next page!
				$this->Session->setFlash('Thank you for logging in!');
				$this->Session->write('User', $user);

				// Go to our first destination!
				$this->Redirect(array('controller' => 'Controller_name', 'action' => 'Action_name'));
				exit();
			}
			else
			{
				$this->Session->setFlash('Incorrect username/password!', true);
				$this->Redirect(array('action' => 'login_form'));
				exit();
			}
		}
	}

	function logout() {

		$this->Session->destroy();
		$this->Session->setFlash('You have been logged out!');

		// Go home!
		$this->Redirect('/');
		exit();
	}
}
?>

Place this code in /app/controllers/users_controller.php

The AppController – Validate on Every Page

class AppController extends Controller {

	// Check if they are logged in
	function authenticate()
	{
		// Check if the session variable User exists, redirect to loginform if not
		if(!$this->Session->check('User'))
		{
			$this->redirect(array('controller' => 'users', 'action' => 'login_form'));
			exit();
		}
	}

	// Authenticate on every action, except the login form
	function afterFilter()
	{
		if( $this->action != 'login_form' )
		{
			$this->authenticate();
		}
	}
}

Now simply place this code in /app/app_controller.php

What does this do? Well, now no matter where anyone goes on your website, they will be redirected to the login page UNLESS they are logged in! Excellent.



Share this post


facebooktwittergoogle_plusredditpinterestlinkedinmail

Tags: ,

15 Responses to “Very Simple User Authentication with CakePHP”

  1. Anonymous Coward Says:

    Shouldn’t the login form be in the users subdirectory and be called login_form.ctp?

  2. Phillip Napieralski Says:

    Fixed! Thanks for pointing that out.

  3. amruth Says:

    i need to make authentication for two different users……one for user(when logged in should be directed to user page) and another for admin(directed to admin page). pls help me out. thanks

  4. Phillip Napieralski Says:

    Hey amruth,

    What you could do in that case is check the group name inside the login() method.

    Here’s an example that might work:
    Change this code in the login() method

    $this->Redirect(array('controller' => 'Controller_name', 'action' => 'Action_name'));
    exit();
    

    to something like

    // Use the user variable returned from the validate login function
    if ($user['Group']['name'] == 'admin') {
    $this->Redirect(array('controller' => 'ControllerName', 'action' => 'AdminActionName'));
    } else {
    $this->Redirect(array('controller' => 'SomeOtherControllerMaybe', 'action' => 'UserAction'));
    }
    

    Finally change the ‘find’ in validateLogin function in the model to

    $user = $this->find(array('username' => $data['username'], 'password' => $data['password']), array('id', 'username', 'Group.name'));
    

    You would also want to make sure that a normal user can’t access an admin page by typing it in the url.

  5. Rose Says:

    Hi… I tried ur code. I got error like

    class AppController extends Controller { // Check if they are logged in function authenticate() { // Check if the session variable User exists, redirect to loginform if not if(!$this-&gt;Session-&gt;check('User')) { $this-&gt;redirect(array('controller' =&gt; 'users', 'action' =&gt; 'login_form')); exit(); } } // Authenticate on every action, except the login form function afterFilter() { if( $this-&gt;action != 'login_form' ) { $this-&gt;authenticate(); } } }
    Fatal error: Class 'AppController' not found in C:\xampp\htdocs\cake\app\controllers\users_controller.php on line 4
    

    I copied ur app_contoller.php program in /app/controllers/app_controller.php

    Plz help me

  6. Rekha Says:

    Hello…
    I tried ur code. But the login button is not working. IT doesnt show any error msg. I dont know where i did mistake.

    Here is my code.

    weee_users_controller.php
    ——————————

    WeeeUser-&gt;recursive = 0;
    		//$this-&gt;set('weeeUsers', $this-&gt;paginate());s
                    $this-&gt;set('posts',$this-&gt;WeeeUser-&gt;find('all'));
    	}
    
    	function view($id = null) {
    		if (!$id) {
    			$this-&gt;Session-&gt;setFlash('Invalid weee user', true);
    			$this-&gt;redirect(array('action' =&gt; 'index'));
    		}		
        $this-&gt;set('post', $this-&gt;WeeeUser-&gt;read(null, $id));
    	}
    
    	function add() {
    		if (!empty($this-&gt;data)) {			
    			if ($this-&gt;WeeeUser-&gt;save($this-&gt;data)) {
    				$this-&gt;Session-&gt;setFlash('The weee user has been saved');
    				$this-&gt;redirect(array('action' =&gt; 'index'));
    			} else {
    				$this-&gt;Session-&gt;setFlash('The weee user could not be saved. Please, try again.');
    			}
    		}               
    	}
    
    	function edit($id = null) {
    		 if (empty($this-&gt;data)) {
    		      $this-&gt;data = $this-&gt;WeeeUser-&gt;read(null, $id);
                        $this-&gt;Session-&gt;setFlash('The weee user could not be saved. Please, try again.');
              }else {
                   if($this-&gt;WeeeUser-&gt;save($this-&gt;data)) {
                    		$this-&gt;Session-&gt;setFlash('The weee user has been saved');
    										$this-&gt;redirect(array('action' =&gt; 'view',$id));
    					} 
    			}
    	}
    
    
    	function delete($id = null) {
    		if (!$id) {
    			$this-&gt;Session-&gt;setFlash('Invalid id for weee user');
    			$this-&gt;redirect(array('action'=&gt;'index'));
    		}
    		if ($this-&gt;WeeeUser-&gt;delete($id)) {
    			$this-&gt;Session-&gt;setFlash('Weee user deleted');
    			$this-&gt;redirect(array('action'=&gt;'index'));
    		}
    		$this-&gt;Session-&gt;setFlash('Weee user was not deleted');
    		$this-&gt;redirect(array('action' =&gt; 'index'));
    	}
    	
    
    	function ajax_login_form() 
    	{
        $this-&gt;layout = 'ajax';
        if (!empty($this-&gt;data)) 
        {
           if ($this-&gt;data['User']['username'] == '') 
              $this-&gt;set('value', 0);
           else 
           {
             	$u = $this-&gt;User-&gt;findByUsername($this-&gt;data['User']['username']);
    
            	if (empty($u)) 
            		 $this-&gt;set('value', 1);
            	else 
            	   $this-&gt;set('value', 0);
           }
        }
        else 
           $this-&gt;set('value', 0);    
    	}
    
    
      //Placeholder for login_form, required by CakePHP to see the login_form view    
      //function login_form() { }     
      
      function login_form() 
      {       
         //Check if they went here after submitting the form        
         // Note that all our form data is preceded by the model name ['WeeeUser']        
         
         if(empty($this-&gt;data['WeeeUser']['username']) == false)        
         {
         echo "im here";
         
            //Here we validate the user by calling that method from the User model            
            if(($user = $this-&gt;WeeeUser-&gt;validateLogin($this-&gt;data['WeeeUser'])) != false)            
            {   
               //Write some Session variables and redirect to our next page!                
               $this-&gt;Session-&gt;setFlash('Thank you for logging in!');                
               $this-&gt;Session-&gt;write('WeeeUser', $user);                 
               
               // Go to our first destination!                
               $this-&gt;Redirect(array('controller' =&gt; 'Weee_users', 'action' =&gt; 'index'));                
               
               exit();            
           }            
           else            
           {           
              $this-&gt;Session-&gt;setFlash('Incorrect username/password!', true);                
              $this-&gt;Redirect(array('action' =&gt; 'login_form'));                
              exit();            
           }                      
         }    
       }     
       
       
       function logout() 
       {    
          $this-&gt;Session-&gt;destroy();        
          $this-&gt;Session-&gt;setFlash('You have been logged out!');         
          
          //Go home!        
          $this-&gt;Redirect('/');        
          exit();    
       }
      
    function hello_world(){	}	
    }
    

    —————————————————————————————–
    weee_user.php
    ——————

    array(
                           'username_must_not_be_blank'=&gt;array('rule'=&gt;'notEmpty','message'=&gt;'This post is missing a username!'),
                           'username _must_be_unique'=&gt;array('rule'=&gt;'isUnique','message'=&gt;'A post with this username already exists!')
                          ),   
            'password'=&gt;array(
                          'password_must_not_be_blank'=&gt;array('rule'=&gt;'notEmpty','message'=&gt;'This post is missing its password!')
                         ),
            'email'=&gt;array(
                           'email_must_not_be_blank'=&gt;array('rule'=&gt;'notEmpty','message'=&gt;'This post is missing its email!'),
                           'email_must_be_unique'=&gt;array('rule'=&gt;'isUnique','message'=&gt;'A post with this email already exists!')
                          )          
        );
    	
    	  function validateLogin($data)    
    	  {        
    	      // Search our database where the 'username' field is equal to our form input.        
    	      // Same with the password (this example uses PLAIN TEXT passwords, you should encrypt yours!)        
    	      // The second parameter tells us which fields to return from the database        
    	      // Here is the corresponding query:        
    	      // "SELECT id, username FROM users WHERE username = 'xxx' AND password = 'yyy'"        
    	      $user = $this-&gt;find(array('username' =&gt; $data['username'], 'password' =&gt; $data['password']));         
    	                          
            if( empty($user) == false )        
                return $user;       
             
            return false;    
        }
    	
    }
    

    ———————————————————————————————————————————–
    app_controller.php
    ———————-

    Session-&gt;check('WeeeUser'))        
       {   
            $this-&gt;redirect(array('controller' =&gt;'WeeeUsers', 'action' =&gt; 'login_form'));   
            exit();        
       }    
    }     
    
    // Authenticate on every action, except the login form    
    function afterFilter()    
    {    
        if( $this-&gt;action != 'login_form' )        
           $this-&gt;authenticate();        
    } 
    */
    
    // Check if they are logged in    
    function authenticate()    
    {     
       // Check if the session variable User exists, redirect to loginform if not        
       if(!$this-&gt;Session-&gt;check('WeeeUser'))        
       {   
            $this-&gt;redirect(array('controller' =&gt;'WeeeUsers', 'action' =&gt; 'login_form'));   
            exit();        
       }    
    }     
    
    // Authenticate on every action, except the login form    
    function afterFilter()    
    {    
        if( $this-&gt;action != 'login_form' )        
           $this-&gt;authenticate();        
    } 
    
    }
    

    ——————————————————————————————————–
    login_form.ctp
    —————–

    create('WeeeUser', array('action'=&gt;'login_form') ); 
    
    // Cake automatically knows, based on the input, to create input fields for these two.
    echo $form-&gt;input('username');
    echo $form-&gt;input('password');
    
    // Create the submit button
    echo $form-&gt;end('Login');
    
    ?&gt;
    

    ——————————————————————————————————

  7. Rose Says:

    Hello…

    I tried ur code.

    If i place my cursor in the login button, i cant able to see the URL in the below.

    I checked my code. login_form.ctp is correct and it passes control to my users_controller.php.

    if(empty($this->data[‘User’][‘username’]) == false)
    {}
    else
    {}

    But this code is not working. Both if and else. I tested by giving echo message. I found its not entering in the loop. If i give echo message in the first line or last line in the program, it prints correctly.

    Will u please help me to fix it?

    function login_form()
    {
    //Check if they went here after submitting the form
    // Note that all our form data is preceded by the model name [‘User’]
    if(empty($this->data[‘User’][‘username’]) == false)
    {

    //Here we validate the user by calling that method from the User model
    if(($user = $this->WeeeUser->validateLogin($this->data[‘User’])) != false)
    {
    //Write some Session variables and redirect to our next page!
    $this->Session->setFlash(‘Thank you for logging in!’);
    $this->Session->write(‘User’, $user);

    // Go to our first destination!
    $this->Redirect(array(‘controller’ => ‘users’, ‘action’ => ‘index’));

    exit();
    }
    else
    {
    $this->Session->setFlash(‘Incorrect username/password!’, true);
    $this->Redirect(array(‘action’ => ‘login_form’));
    exit();
    }
    }

  8. Phillip Napieralski Says:

    Rose and Rehka,

    I’m a bit rusty with my CakePHP, but it looks like your guys’ posted code handles user login inside the login_form function in the UsersController. Did you intend to put it in the login() function as the tutorial suggests? Also, what version of CakePHP do y’all use?

    Recall that the login_form.ctp view will call our controllers login(..) function. Thus, the code should be inside your login(..) function instead of login_form(..).

    Let me know if that helps.

  9. Phillip Napieralski Says:

    Also, for long snippets of code, you can use the tags [ Sourcecode lang=”php” ] and [ / Sourcecode ] (both without the spaces).

  10. Rekha Says:

    Now its working for me…. Thanks for your kind help…

  11. Rahul Tailwal Says:

    hey if u want to make ur app_controller then it should be placed in app folder not under app/controller folder. all the controller will use ur app_controller functions. hope u understand it.

  12. Anjana Says:

    I have tried this code but got error like “Array to string conversion [CORE/Cake/Model/Datasource/DboSource.php, line 460]”…

  13. Phillip Napieralski Says:

    Hi Anjana,

    This post was created pre-CakePHP 2.0 (over 3 years ago now). If you are running any modern version of CakePHP, you’ll have to update the code appropriately to work with it.

    Sorry I can’t help more than that!
    Phil

  14. admi Says:

    Warning (2): include(C:\Program Files (x86)\EasyPHP-5.3.8.0\www\gestion_conferen\cakephp\gestionconf\app\View\Rapporteurs\login_form.ctp) [function.include]: failed to open stream: Permission denied [CORE\Cake\View\View.php, line 923]

    Warning (2): include() [function.include]: Failed opening ‘C:\Program Files (x86)\EasyPHP-5.3.8.0\www\gestion_conferen\cakephp\gestionconf\app\View\Rapporteurs\login_form.ctp’ for inclusion (include_path=’C:\Program Files (x86)\EasyPHP-5.3.8.0\www\gestion_conferen\cakephp\gestionconf\lib;.;C:\php\pear’) [CORE\Cake\View\View.php, line 923]

    i need to make authentication help me out. thanks

  15. admi Says:

    how to make an association between two tables (Rapporteurs and Reports)

    it does not work

    class User extends AppModel {
    public $hasMany = array(
    ‘MyRecipe’ => array(
    ‘className’ => ‘Recipe’,
    )
    );