Simple Form Authentication in 1.2.x.x

by 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.


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:

<?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:

<?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:


<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:


<?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.

Report

More on Tutorials

Advertising

Comments

  • keyuribw posted on 11/22/10 07:49:54 AM
    i can not see any login link in my browser after i login.
    i think that the login function is not redirecting to index.ctp
    please help me out....
  • eduardoantonio posted on 10/21/10 11:13:04 AM
    that was awesome, short and very usefull for beginners like me, using your code to make a scaffold only if the user is admin, but cake gives me the next error :

    Private Method in UsersController

    Error: UsersController::__validateLoginStatus() cannot be accessed directly.

    Notice: If you want to customize this error message, create game-web-site\views\errors\private_action.ctp

    this is the code:

    php
    class GamesController extends AppController{
    var $name = 'Games';
    var $scaffold;

    function beforefilter(){
    $this->requestAction(array('controller'=>'users','action'=>'__validateLoginStatus'))
    }
    }
  • eduardoantonio posted on 10/21/10 11:06:22 AM
    that was awesome, short and very usefull for beginners like me, using your code to make a scaffold only if the user is admin, but cake gives me the next error :

    Private Method in UsersController

    Error: UsersController::__validateLoginStatus() cannot be accessed directly.

    Notice: If you want to customize this error message, create game-web-site\views\errors\private_action.ctp

    this is the code:

    requestAction(array('controller'=>'users','action'=>'__validateLoginStatus'))
    }
    }
    ?>
  • eduardoantonio posted on 10/21/10 11:01:35 AM
    that was awesome, short and very usefull for beginners like me, using your code to make a scaffold only if the user is admin, but cake gives me the next error :

    Private Method in UsersController

    Error: UsersController::__validateLoginStatus() cannot be accessed directly.

    Notice: If you want to customize this error message, create game-web-site\views\errors\private_action.ctp

    this is the code:

    requestAction(array('controller'=>'users','action'=>'__validateLoginStatus'))
    }
    }
    ?>
    Can you tell me please how to solve this problems, you can write me at eduardo.antonio@insmet.cu, i hope you or anyone in this post can help me, thanks.
  • rsteuber posted on 05/17/10 10:59:33 AM
    Hi there,

    I was trying to build a 'login for user' with this article, and i get the following errormessage when tested it:

    Parse error: syntax error, unexpected T_STRING in
    (mydomain)\app\controllers\users_controller.php on line 12

    Does anybody know what's so unexpected on
    $this->__validateLoginStatus(); ???

    Here is the code:
    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('U bent ingelogged.');
    $this->redirect('index');
    exit();
    }
    else
    {
    $this->Session->setFlash('Helaas, de ingevoerde gegevens zijn incorrect.');
    exit();
    }
    }
    }

    function logout()
    {
    $this->Session->destroy('user');
    $this->Session->setFlash('U bent uitgelogd!');
    $this->redirect('login');
    }

    function __validateLoginStatus()
    {
    if($this-action != 'login' && $this->action != 'logout')
    {
    if($this->Session->check('User') == false)
    {
    $this->redirect('login');
    $this->Session->setFlash('U moet ingelogd zijn!');
    }
    }

    }
    }
    ?>
    Greetings,
    Rsteuber

  • washington posted on 04/19/10 02:02:21 AM
    How to call beforeFilter in a Controller?

    I saved the source code in the example as login_controller, and
    When I tried this requestAction('login/beforeFilter') from another Controller,

    It said beforeFilter is not defined in LoginController.

    Why?
  • mjamesd posted on 02/01/08 02:37:48 PM
    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.
  • mjamesd posted on 01/25/08 08:38:17 AM
    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';

            }

        }

    }



    ?>
  • Aonxe posted on 10/14/07 11:56:31 AM
    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.');
                }
            }
        }
  • gevaspm posted on 03/21/07 02:57:31 PM
    Thanks, you help so much a beginner like me.
  • hepper posted on 01/14/07 04:42:37 PM
    Just trying to build this functions in my app. Please continue with more articles in this topic :)
  • pixol posted on 11/30/99 12:00:00 AM
    I would like to see a more advanced authentication also how to handle reffer url's
  • Maff posted on 11/30/99 12:00:00 AM
    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.
login to post a comment.