Simple Form Authentication in 1.2.x.x

By Sean Callan (SeanCallan)
There has been a lot of questions about user authentication lately so I thought it was appropriate to create a tutorial that covered the very basics

In this tutorial we'll cover all aspects of MVC in an authentication role, logging a user in, maintaining a session, and restricting unauthorized access to controller actions.

Though this article is based on CakePHP 1.2.x, the same concepts apply in 1.1.x and most of the code can be reused.

Section 1 - Database

First, we've got to create a table in our database and configure the app/database.php file. I'll leave this step up to you, but I've included a basic schema below to get you started.

Download code
DROP TABLE IF EXISTS `users`;

CREATE TABLE `users` (
  `id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(20) NOT NULL,
  `password` VARCHAR(40) NOT NULL,
  `email` VARCHAR(40) NULL,
  `created` DATETIME NULL,
  PRIMARY KEY(`id`)
);

INSERT INTO `users` (`id`, `username`, `password`, `email`, `created`) VALUES ('1', 'newuser', MD5('test'), 'user@domain.com', NOW());

As you can see we're manually creating our test user account: newuser, password: test.

Section 2 - Model

app/models/user.php
Now we're going to cover creating our User model. Since we're just checking user information the model is pretty basic. But all logic and calculations in an MVC environment should take place inside the model, so you'll see the function validateLogin($data) below.

Function validateLogin() is responsible for determining if the information supplied was correct and then returning to us the id and username to be stored in a session for use later, if the information is incorrect, we return false. Error handling will occur in the controller.

Model Class:

Download code <?php 
class User extends AppModel
{
    var 
$name 'User';
    
    function 
validateLogin($data)
    {
        
$user $this->find(array('username' => $data['username'], 'password' => md5($data['password'])), array('id''username'));
        if(empty(
$user) == false)
            return 
$user['User'];
        return 
false;
    }
    
}
?>

Section 3 - Controller

app/controllers/users_controller.php
Next up is our controller. Don't be afraid if you see some things you're not used to, I'm going to step through each piece one by one and explain them.

Controller Class:

Download code <?php 
class UsersController extends AppController
{
    var 
$name "Users";
    var 
$helpers = array('Html''Form');
    
    function 
index()
    {
        
    }
    
    function 
beforeFilter()
    {
        
$this->__validateLoginStatus();
    }
    
    function 
login()
    {
        if(empty(
$this->data) == false)
        {
            if((
$user $this->User->validateLogin($this->data['User'])) == true)
            {
                
$this->Session->write('User'$user);
                
$this->Session->setFlash('You\'ve successfully logged in.');
                
$this->redirect('index');
                exit();
            }
            else
            {
                
$this->Session->setFlash('Sorry, the information you\'ve entered is incorrect.');
                exit();
            }
        }
    }
    
    function 
logout()
    {
        
$this->Session->destroy('user');
        
$this->Session->setFlash('You\'ve successfully logged out.');
        
$this->redirect('login');
    }
        
    function 
__validateLoginStatus()
    {
        if(
$this->action != 'login' && $this->action != 'logout')
        {
            if(
$this->Session->check('User') == false)
            {
                
$this->redirect('login');
                
$this->Session->setFlash('The URL you\'ve followed requires you login.');
            }
        }
    }
    
}

?>

Let's break the controller down into nice small pieces and explain each.

First thing you may have noticed is the function beforeFilter(), when present this function is called before each controller action. So we'll use it to call our function __validateLoginStatus() to ensure that an individual is properly logged in.

Next, we'll look at function login(), this is our golden child. In this function we take information from the user and have it validated and handle it accordingly. You've probably noticed we're calling function validateLogin in login: this is where the actual authentication takes place. When the user has entered the correct information we create a session ('User') and store the information for later use. The use of sessions is a fantastic way to determine if a user is actively logged in, hence why it was chosen for this tutorial. You'll also notice in login we redirect the user and setFlash, pretty basic things, so I won't cover them in any great detail.

Now we see function logout() and it's pretty straight forward. If we're using sessions to determine if a person is actively logged in, just destroy it to log them out and that's exactly what we're doing. Give them a nice message, and send them on their way. If you're a little confused as to why we're doing this, I'll explain a little more next as I explain the function _validateLoginStatus().

There are a few things about function __validateLoginStatus() that may be foreign to new users. First, the leading underscores (`__`), this denotes this function as private and therefore no view is required. Second may be the use of $this->action, which returns the current action in the controller. For this example we use it for exceptions, when a user isn't required to log in. Because the function is called in beforeFilter(), it will be executed before each and every action of the controller (put a beforeFilter in app/app_controller.php and it's called before every controller's actions). The function is responsible for detecting a 'User' session and redirecting users who are trying to access restricted pages. We use exceptions for pages like login and logout which otherwise result in errors.

Section 3 - Views

app/views/users/index.ctp & app/views/users/login.ctp

The following views are straight forward and should be customized to fit your site needs. I won't go into the views since they contain self explanatory code.

login.ctp
Present the user with a form for their username and password. Simple as that!

View Template:

Download code
<div class="login">
<h2>Login</h2>    
    <?php echo $form->create('User', array('action' => 'login'));?>
        <?php echo $form->input('username');?>
        <?php echo $form->input('password');?>
        <?php echo $form->submit('Login');?>
    <?php echo $form->end(); ?>
</div>

index.ctp
You should always include a link or button for logging out a user manually. Aside from that, in this example index is for restricted access, have your way with it.

View Template:

Download code
<?php echo $html->link('Logout', array('controller' => 'Users''action' => 'logout')); ?>
<br/><br/>
You've accessed the secret secure location!


Section 4 - Conclusion

This tutorial should provide you with a basic understanding how to manage user logins and restrict access to certain pages based on login status. This article is in no way meant for an advanced audience, but rather to help those who have some experience with Cake but are unfamiliar with authentication and beforeFilters.

If there is enough interest in the topic, I will continue with a series of articles moving towards a more advanced authentication system with groups and permissions.

 

Comments 212

CakePHP Team Comments Author Comments
 

Comment

1 Grate articel

Just trying to build this functions in my app. Please continue with more articles in this topic :)
Posted Jan 14, 2007 by Henrik Persson
 

Question

2 Yes

I would like to see a more advanced authentication also how to handle reffer url's
Posted Dec 31, 1969 by chris
 

Comment

3 Helpful tutorial for newbes

Thanks for this tutorial. I've been playing around with cakePHP for a few days now, and although this tutorial isn't very long, I do think it's of great value for new users. Due to the clear writing, and not to complex examples it's easy to understand what's going on.

I find it comforting to see you place some logic code in the User model, this makes much more sense that to place that kind of code in the controller (as many tutorials do, and even the manual does).
Also the note about the leading underscores ('__') was something I find very useful, because I was puzzled by the way that every function inside a controller would generate a view. (maybe I've missed that part in that manual, or should I've read the PHP manual better?)

I think the only this tutorial is missing would be a explanation about what code to place in other controllers in order to restrict a action (or access to the whole controller).

If there is enough interest in the topic, I will continue with a series of articles moving towards a more advanced authentication system with groups and permissions. To bad this tutorial hasn't any comments so far, you really deserve more. I think a continuation would be awesome, and helpful to others.
Posted Dec 31, 1969 by Ruud
 

Comment

4 Thank You

Thanks, you help so much a beginner like me.
Posted Mar 21, 2007 by Gevan Schaefer Pereira Martins
 

Comment

5 Registering

Here is a simple script that adds a register function to your application.


    function register() {
        if (!empty($this->data)) {
            $this->cleanUpFields();
            $this->data['User']['password'] = md5($this->data['User']['password']);
            $this->User->create();
            if ($this->User->save($this->data)) {
                $this->Session->write('User', $this->User->findByUsername($this->data['User']['username']));
                $this->Session->setFlash('Thank you for registering.');
                $this->redirect('/');
            } else {
                $this->Session->setFlash('The User could not be saved. Please, try again.');
            }
        }
    }
Posted Oct 14, 2007 by Steven Holder
 

Comment

6 beforeFilter code

This is what I used in my app_controller to make it check login status on admin pages:


<?php



class AppController extends Controller {

    var 
$helpers = array('Html''Form''Javascript');

    

    function 
beforeFilter() {

        if (
$this->params['admin'] == true) {

            if(
$this->Session->check('User') == false) {

                
$this->redirect('/login');

                
$this->Session->setFlash('The URL you\'ve followed requires you login.');

            }

            
$this->layout 'admin';

        }

    }

}



?>
Posted Jan 25, 2008 by Mark Drummond
 

Comment

7 beforeFilter code

Here's what I have for a beforeFilter function in my app_controller.php:


    function beforeFilter() {

        if (isset($this->params['admin'])) {

            if($this->Session->check('User') == false) {

                $this->redirect('/users/login');

                $this->Session->setFlash('The URL you\'ve followed requires you login.');

            }

            $this->layout = 'admin';

        }

    }

Basically, check for the session variable and redirect if you're not logged in.
Posted Feb 1, 2008 by Mark Drummond