obAuth Component Tutorial

by coeus
This is a tutorial on how to use obAuth component: http://bakery.cakephp.org/articles/view/130
This is a simple authentication component that I wrote as a quick way to protect areas of your web application. I wanted manual control of what permissions are granted to what action. Something as simple as this: <?php $this->obAuth->lock(array(1)); ?>

At the top of my action should mean only users belonging to usergroup 1 are allowed to use this action.


Before we begin this tutorial, it's important to note that this component is in it's alpha stages. I would love feedback from some of the more experienced bakers out there. Enjoy


1. Download the component
2. Recommended database tables
3. Include the component and create models
4. Required actions
5. Start securing our private areas!
6. Extra Tips & Tricks


1. Download the component


Get the component here: http://bakery.cakephp.org/articles/view/130


2. Recommended database tables


Essentially you'll want a users table and a groups table. The users table will hold the regular user data and a foreign key called "group_id". The groups table is where you store your groups. Your tables should look like the following:



-- --------------------------------------------------------

-- 
-- Table structure for table `groups`
-- 

CREATE TABLE `groups` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(50) NOT NULL default '',
  `created` datetime NOT NULL default '0000-00-00 00:00:00',
  `modified` datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT;

-- 
-- Dumping data for table `groups`
-- 

INSERT INTO `groups` (`id`, `name`, `created`, `modified`) VALUES (1, 'Member', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `groups` (`id`, `name`, `created`, `modified`) VALUES (2, 'Admin', '0000-00-00 00:00:00', '0000-00-00 00:00:00');

-- --------------------------------------------------------

-- 
-- Table structure for table `users`
-- 

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `username` varchar(50) NOT NULL default '',
  `password` varchar(32) NOT NULL default '',
  `fname` varchar(50) NOT NULL,
  `lname` varchar(50) NOT NULL,
  `email` varchar(100) NOT NULL default '',
  `group_id` int(10) unsigned NOT NULL default '0',
  `active` tinyint(1) unsigned NOT NULL default '0',
  `created` datetime NOT NULL default '0000-00-00 00:00:00',
  `modified` datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (`id`),
  KEY `group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT;

You can do whatever you want with your users table as long as the fields id, group_id, username, password and active are there


3. Include the component and create models


Ok, this is relatively easy. Once you've downloaded the component you need to place it in your /app/controllers/components directory. Once you've placed it in the right directory you need to call it in your controller file. So let's use posts_controller.php as an example:


Model Class:

<?php 
class PostsController extends AppController 
{
    var 
$name 'Posts';
    var 
$components = array("obAuth");
}
?>

Next you should have a User model and a Group model with the proper associations. Users belong to a group and groups has many users. Please refer to this tutorial if you don't understand how to setup associations go here: http://wiki.cakephp.org/docs:understanding_associations


That's it. You can now start using the obAuth component.

4. Required actions


To begin using the obAuth component we need to have to important actions. "Login" and "Logout". They are self-explanatory, so I don't need to tell you what they're for. Instead I'll show you how mine look:


In /app/controllers/users_controller.php


<?php
function login()
{
    if(isset(
$this->data['User']))
    {
        if(
$this->obAuth->login($this->data['User']))
        {
            
$this->redirect('/users');
        }
        
$this->flash("Username/Password is incorrect");
    }
}

function 
logout()
{
    
$this->obAuth->lock();
    
$this->obAuth->logout();
    
$this->flash('You are now logged out.');
    
$this->redirect('/');
}
?>

Notice I use a method obAuth::login($data). This method is used to check the data posted with a user in the database. If one exists then the user will be authenticated


Now for the view you really only need one for the login action because logout just redirects. So the view should look something like this


View Template:


<?php echo $html->formTag('/users/login')?>
<fieldset>
    <legend>User Login</legend>
    
        <label for="username">Username: </label>
        <?php echo $html->input('User/username', array('style' => 'width: 150px'))?><br />
    
        <label for="password">Password: </label>
        <?php echo $html->password('User/password', array('style' => 'width: 150px'))?><br />
    
        <label for="submit"> </label><br />
        <?php echo $html->submit('Sign In')?>    
</fieldset>
</form>

5. Start securing your private areas


Alright, now that we have our login and logout actions we can get users authenticated. So let's start securing our actions by setting the permissions. Let's say we have an action in our Posts controller called "add" and we only want users from the group id "3" allowed to use it. Well this is all we need to do to secure the action



<?php
function add()
{
    
$this->obAuth->lock(array(3)); // Only users with the group_id '3' are allowed here

    
if (!empty($this->data))
    {
        if (
$this->Post->save($this->data))
        {
            
$this->flash('Your post has been saved.','/posts');
        }
    }
}
?>

Now notice that 1 line of code used to secure this action. It uses obAuth::lock($groups=null, $redirect=null). The array passed is an array of group ids allowed to access this action. So if you had Super Administrators (3) and Administrators (2) and wanted them to have access to this action you would write:

$this->obAuth->lock(array(2,3));.

If you leave $groups parameter empty then it'll allow any AUTHENTICATED user to access the action. So you're still securing it from anonymous visitors. The $redirect parameter allows you set a url that will redirect a user who's not allowed to access the action. If you leave it empty it will redirect the user to the login page.

Report

More on Tutorials

Advertising

Comments

  • juansaab posted on 03/18/09 09:46:22 PM
    I'm a new cooker. I'm building my first aplication, and really see a very large way in the autentication with ACL and his (ARO's, ACO's). Do you think that I simply can use your component for autenticatión and dont develop over ACL?¿?¿.

    Your component runs fine on 1.2 Version?
    Thanks,
  • infantigniter posted on 06/09/08 09:09:42 PM
    Warning: array_merge() [function.array-merge]: Argument #2 is not an array in /home2/zendscom/public_html/cake/cake/libs/controller/component.php on line 58

    Warning: Invalid argument supplied for foreach() in /home2/zendscom/public_html/cake/cake/libs/controller/component.php on line 85
    CakePHP Rapid Development
    Missing Model

    No class found for the Login model

    Notice: If you want to customize this error message, create app/views/errors/missing_model.thtml.

    Fatal: Create the class below in file : app/models/login.php

    class Login extends AppModel {
    var $name = 'Login';
    }


    When I try to access login. I'm new to Cake, but users.php & login.php both are not "found" as models for me.
    • riledhel posted on 06/25/08 10:57:19 PM
      Warning: array_merge() [function.array-merge]: Argument #2 is not an array in /home2/zendscom/public_html/cake/cake/libs/controller/component.php on line 58

      Warning: Invalid argument supplied for foreach() in /home2/zendscom/public_html/cake/cake/libs/controller/component.php on line 85
      CakePHP Rapid Development
      Missing Model

      No class found for the Login model

      Notice: If you want to customize this error message, create app/views/errors/missing_model.thtml.

      Fatal: Create the class below in file : app/models/login.php

      class Login extends AppModel {
      var $name = 'Login';
      }


      When I try to access login. I'm new to Cake, but users.php & login.php both are not "found" as models for me.

      You definetely should have a Users model in /app/models/user.php and also a Group model in /app/models/group.php Inside the User's controller (not the model, this should be saved in /app/controllers/users_controller.php) you should include the login() and logout() methods with the following code:


      <?php
      function login()
      {
          if(isset(
      $this->data['User']))
          {
              if(
      $this->obAuth->login($this->data['User']))
              {
                  
      $this->redirect('/users');
              }
              
      $this->flash("Username/Password is incorrect"); // $this->Session->setFlash("Username/Password is incorrect"); for newer versions of the framework
          
      }
      }

      function 
      logout()
      {
          
      $this->obAuth->lock();
          
      $this->obAuth->logout();
          
      $this->flash('You are now logged out.'); // $this->Session->setFlash('You are now logged out.'); for newer versions of the framework
          
      $this->redirect('/');
      }
      ?> 

      Then try again and see if it works :)
  • cmiquel posted on 01/02/08 10:59:19 AM
    I can't get the component to work correctly. For some reason I can't get the authentication to stay logged in. When I login in it takes me to the correct default page, but when I try to navigate anywhere else or refresh the same default page I am redirected to login as if I was never logged in.

    Any ideas?
  • adyshev posted on 12/17/07 08:37:46 AM
    ...
    var $components = array('Session','RequestHandler');
    ...
    if(!$hasAccess)
    {

    if (!$this->RequestHandler->isAjax()){
    $page = (!empty($redirect)) ? $redirect : $this->login_page;
    $this->controller->redirect($page);
    } else {
    $this->controller->render('..'.$this->login_page,'ajax');
    }

    }
    ...
    that`s all.
  • rajasa posted on 11/17/07 01:54:45 PM
    thx james! work like a charm
  • jrevillini posted on 11/17/07 11:01:42 AM
    replace all $this->flash with $this->Session->setFlash if you're getting errors. I'm using CakePHP 1.1.18.
  • Bo_oz posted on 11/15/07 05:27:25 AM
    Is there a solution to fix this for 1.18, I was just considering upgrading until I read this post. I'm using this component for security, so it would be very unfortunate if this component stops working.
  • rajasa posted on 11/05/07 04:04:07 PM
    This component are not working in ver 1.1.18, probably because they change the way sessions are handled.

    Anybody know how to fix this?
  • tom_m posted on 11/05/07 01:01:59 PM
    Bo is correct, there is a huge security hole here. I found this out the hard way with a client calling up saying their data was gone!

    YES it sucked BIG time. However it was a very easy fix (thanks to Bo's advice) and simply putting an exit(); after the redirect in the lock method of ob_auth.php (this is after it does the whole redirect if you don't have access, before that if statement closes..so the if(!$hasAccess)...part) solves the issue quite nicely.

    If someone directly puts in www.com/controller/delete/5 for example, it will direct them to log in and all will be fine.

    This is a MAJOR security issue that I'm glad it was simple to fix.
  • jdsilveronnelly posted on 10/23/07 01:22:28 PM
    It says that "Users belong to a Group" but why not make it so that Users to Groups is a HABTM relationship? Or, more succinctly, my question is

    Is it possible to use this auth component with Users -> HABTM -> Groups so that a User can belong to more than one group?

    It seems like authentication would be more flexible this way.

    Or is there an existing authentication component that does this?


    Thanks. Later...
  • Bo_oz posted on 09/14/07 12:21:58 PM
    I think there is a mistake in your Authentication component, at least, it seems that implementing it the way you say does not seem to be secure, let me explain the case.


    When calling an action, it seems that although the lock method tries to redirect the browser (to the deny page) the controller does finish the remaning code before the actual redirection takes place!!!

    This means that calling for instance http://domain.com/blog/delete/3
    Does not require me to be logged in to delete the blog with id=3 although the first line of code in this action is:

    $this->obAuth->lock(array(1));

    .........

    Ofcourse you are able to fix this problem by letting the lock method return true/false and using an if statement in your action to determine the access of the user

    function action() {
    if($this->obAuth->lock(array(1))) {
    do something securely!
    }
    }

    Also you have to add: "return $hasAccess;" just before the closing } of the lock method inside the obAuth component.
    • Bo_oz posted on 09/14/07 12:29:44 PM
      The problem could also be fixed by adding exit(); after the redirect in the lock method, this keeps the controller action from finishing the remaining lines of code! So this means only the line needs to be called without the if() statement.

      Could you please fix this is the component on the linked page, because this can cause serious problems, since it is easily missed!
  • Bo_oz posted on 09/14/07 07:27:33 AM
    Hi,

    I receive the following error, while literally following yout tutorial:


    Notice: Trying to get property of non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 72

    Notice: Trying to get property of non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 74

    Fatal error: Call to a member function redirect() on a non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 96
    • cakemed posted on 10/23/07 09:13:24 AM
      Hi, I receive exactly the same error as Bo...
      in the lines i have:
      72: if ($this->controller->action != "login")
      74: $this->last_page = $this->controller->here;
      96: $this->controller->redirect($page);
      Might Ob or someone else help me here??

      Hi,

      I receive the following error, while literally following yout tutorial:


      Notice: Trying to get property of non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 72

      Notice: Trying to get property of non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 74

      Fatal error: Call to a member function redirect() on a non-object in /var/www/vhosts/iamin.com/subdomains/cms/httpdocs/app/controllers/components/ob_auth.php on line 96
  • convan23 posted on 07/17/07 04:30:08 PM
    I am getting the following error when trying to implement this component:

    Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /home/6813/domains/cheesecake.convandesign.com/html/app/controllers/components/ob_auth.php:118) in /home/6813/domains/cheesecake.convandesign.com/html/cake/libs/session.php on line 146

    Warning: Cannot modify header information - headers already sent by (output started at /home/6813/domains/cheesecake.convandesign.com/html/app/controllers/components/ob_auth.php:118) in /home/6813/domains/cheesecake.convandesign.com/html/cake/libs/session.php on line 147
    • coeus posted on 08/04/07 01:46:36 AM
      Make sure you don't have any spaces before or after the tags.
  • Vladislav posted on 04/10/07 03:54:08 AM
    It's small helper (sorry, I wrote them at 1:30 am ;-)
    And suddenly Session->valid - doesn't work

    But in view you can get TRUE, or FALSE and combine your view elements for admin/members/etc.

    class obAuthHelper extends Helper {

    var $helpers = array (
    'Session'
    );

    var $sesskey = "mYpERsOnALhaSHkeY";

    // Check is user is part of usergroup specified
    function lock($groups=null)
    {

    $hasAccess = false;

    if ($this->Session->check($this->sesskey))
    {
    $this->user = $this->Session->read($this->sesskey);
    }

    if (!empty($this->user))
    {
    if (!empty($groups))
    {
    foreach ($groups as $group)
    {

    if ($this->user["Group"]['id'] == $group || $this->user["Group"]['name'] == $group)
    $hasAccess = true;
    }
    }
    else
    {
    $hasAccess = true;
    }
    }



    if ($hasAccess) return 1;
    else return 0;

    }

    }
  • brightstorm posted on 11/28/06 04:37:05 AM
    thanks for getting the obauth link back up.
  • brightstorm posted on 11/27/06 11:19:33 AM
    hi steve
    i've been recommended to use obauth by a friend of mine.

    the link to http://bakery.cakephp.org/articles/view/130 is inactive. has this auth method evolved to something better or is it gone for good?
  • biesbjerg posted on 11/20/06 12:52:25 PM
    As Aram experienced, there's a bug in the obAuth code.

    You can replace:

    $usergroup = $this->user->usergroup;

    With:

    $usergroup = $this->user['Group']['id']; // fix

    Or why not use the "function getGroupId()" in the code?

    $usergroup = $this->getGroupId(); // fox
    • coeus posted on 11/22/06 01:44:19 PM
      Thanks, I've spotted the bug and just removed that line altogether because it wasn't being used.

      Nate:

      Yes, that's a good way of doing it as well and I may change it to that way. As mentioned this is still in alpha stages, haven't had time recently to tweak it, but will be doing so soon.

      Ultimately I want the option for it to be controlled manually (Put in the permissions right in the code) or automatically (handle permissions through admin interface and storing them in DB)
  • nate posted on 11/17/06 12:42:39 AM
    Wouldn't this require a lot less code if the action locking was defined as an associative array, keyed off of the action name? That seems a lot less distributed/redundant than having to put in a call to lock() in every single action.

    Then you could actually use the component callback interface as designed, i.e.: having the startup() method do the rejecting passively: no further interaction required on the part of the programmer.
  • aranworld posted on 11/16/06 03:44:21 PM
    First, thanks for this. It is a real help and it has helped me out a lot in understanding CakePHP!

    When I tried this tutorial out, it worked, but I got the Notice:

    Notice: Trying to get property of non-object in C:\web\aclusc-leg\app\controllers\components\ob_auth.php on line 73

    Line 73 was:

    $usergroup = $this->user->usergroup;

    Its seems as if the line in obAuth::startup()

    $this->user = $this->Session->read($this->sesskey);

    creates an array rather than an object. So in my test, $this->user->usergroup does not exist, but I could get a usergroup id by using:

    $usergroup = $this->user['Group']['id'];

    Is there something I'm missing? Should $this->user be an object with the property usergroup?
  • Synchro posted on 11/10/06 08:29:36 AM
    How should I call lock() inside a view? For example, in an index view I might want to only show an add link to some users, but everything else should remain the same.
    • coeus posted on 11/12/06 04:11:46 PM
      How should I call lock() inside a view? For example, in an index view I might want to only show an add link to some users, but everything else should remain the same.
      Lock is more for preventing actions from being run from unauthorized user/groups.

      If a user has authorization to use an action but shouldn't see certain links, you'll have to put if statements in your view where user ID and group ID are the conditions in it to only show the link to authorized users.

  • zomg posted on 11/05/06 04:25:06 AM
    Can you elaborate on the associations part?
  • infectedrhythms posted on 11/30/99 12:00:00 AM
    according to the login_hash in the login function of the component.
  • Natcon67 posted on 11/30/99 12:00:00 AM
    Does anyone know of a way to use $this->obAuth->lock() in the controller beforeFilter?
    • KsuZ posted on 06/23/07 09:56:33 AM
      Does anyone know of a way to use $this->obAuth->lock() in the controller beforeFilter?
      I use something like this

      function beforeFilter(){
      $this->obAuth->startup($this);
      if(isset($this->params['admin']))
      {
      $this->obAuth->lock(array(1,2));
      }
login to post a comment.