How to use AuthComponent in CakePHP
Wherever I went - irc, trac, google groups and certain blogs - I noticed people complaining about either not knowing how to use the AuthComponent or having problems with it.
After reading the only tutorials available across all usual blogs I rely on for all my CakePHP needs, I realized their was a gap I could fill. The reasons are:
- Lack of a CPC way for quick and easy implementation.
- No real explanation or solution for using in a real application environment (multiple controllers, beforeFiters, actions, etc.).
- Not covering the different authorization methods it’s capable of: default, controller, actions, crud, model and object
CPC way for easy implementation
This is to make it fairly simple for anyone to see, understand and configure the different constants included in AuthComponent. All the descriptions were taken directly from the component’s source code to have everything in one place when setting it up.
class CustomController extends AppController
{
var $components = array('Auth');
function __setAuth()
{
if (isset($this->Auth))
{
/**
* The name of an optional view element to render when an Ajax request is made
* with an invalid or expired session
*
* @var string
*/
$this->Auth->ajaxLogin = null;
/**
* The name of the model that represents users which will be authenticated. Defaults to 'User'.
*
* @var string
*/
$this->Auth->userModel = 'User';
/**
* Additional query conditions to use when looking up and authenticating users,
* i.e. array('User.is_active' => 1).
*
* @var array
*/
$this->Auth->userScope = array();
/**
* Allows you to specify non-default login name and password fields used in
* $userModel, i.e. array('username' => 'login_name', 'password' => 'passwd').
*
* @var array
*/
$this->Auth->fields = array('username' => 'username', 'password' => 'passwd');
/**
* the hash function to use, options: sha1, sha256, md5
*
* @var string
*/
$this->Auth->hash = 'sha1';
/**
* The session key name where the record of the current user is stored. If
* unspecified, it will be "Auth.{$userModel name}".
*
* @var string
*/
$this->Auth->sessionKey = null;
/**
* If using action-based access control, this defines how the paths to action
* ACO nodes is computed. If, for example, all controller nodes are nested
* under an ACO node named 'Controllers', $actionPath should be set to
* "Controllers/".
*
* @var string
*/
$this->Auth->actionPath = null;
/**
* A URL (defined as a string or array) to the controller action that handles
* logins.
*
* @var mixed
*/
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
/**
* Normally, if a user is redirected to the $loginAction page, the location they
* were redirected from will be stored in the session so that they can be
* redirected back after a successful login. If this session value is not
* set, the user will be redirected to the page specified in $loginRedirect.
*
* @var mixed
*/
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'dashboard');
/**
* The the default action to redirect to after the user is logged out. While AuthComponent does
* not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
* Defaults to AuthComponent::$loginAction.
*
* @var mixed
*/
$this->Auth->logoutRedirect = null;
/**
* The name of model or model object, or any other object has an isAuthorized method.
*
* @var string
*/
$this->Auth->object = null;
/**
* Error to display when user login fails. For security purposes, only one error is used for all
* login failures, so as not to expose information on why the login failed.
*
* @var string
*/
$this->Auth->loginError = 'Login failed. Invalid username or password.';
/**
* Error to display when user attempts to access an object or action to which they do not have
* acccess.
*
* @var string
*/
$this->Auth->authError = 'You are not authorized to access that location.';
/**
* Determines whether AuthComponent will automatically redirect and exit if login is successful.
*
* @var boolean
*/
$this->Auth->autoRedirect = true;
/**
* Controller actions for which user validation is not required.
*
* @var array
*/
$this->Auth->allowedActions = array();
}
}
function beforeFilter()
{
$this->__setAuth();
}
}
Simple implementation for real application usage
Here are the things I believed it should cover to best serve all needs in the application:1. By default, all pages should have all actions allowed. The implementation inside app_controller should be transparent, meaning that if I don’t want AuthComponent in a CustomController, I don’t have to do anything to disable it.
2. Avoid using the CustomController->beforeFilter() callback. Enabling the AuthComponent should be as easy as setting $components, $pageTitle, etc. inside the controller.
3. Simplify big controllers’ actions management. Checking for authorization on just a couple actions in a big controller where everything else shouldn’t be checked currently requires that you list all the actions you want to allow, what about listing only the ones you want to deny? Imagine a controller with 25 actions where only 4 have to be authorized, right now you’d need to set the 21 actions you don’t want to authorize.
4. Enable wildcard (*), single action (string) or a list of actions (array). To follow with the main CakePHP way of doing things, the Auth constants used to enable/disable the component should accept both string and array values.
5. Never have wildcard () be the value for Auth->allowedActions. Right now, Auth->deny(’actionName’) used with Auth->allow() (which makes CakePHP sets the Auth->allowedActions to ‘‘) or without it at all, is useless. Now, since in number 3 I say denying only a couple actions in a big controller are made easier, the only reason for this to still be a requirement is for the exceptional cases when you need to further customize the way AuthComponent is set in your CustomController’s beforeFilter() callback with just a couple of lines.
The code
You will first need to include Auth to the components your AppController calls for./app/app_controller.php
var $components = array('Auth');
Then add those 2 constants to the same file.
/** * Methods where public is allowed access * * @access public * @var array */ var $authAllow = array();authAllow: Optional, mixed. Use that if you want to add, on the CustomController level, to the default Auth->allowedActions you have set./** * Methods where public is denied access * * @access public * @var array */ var $authDeny = array();
authDeny: Optional, mixed. Use that for all the actions you wish to check for authorization before proceeding.
Now the real coding part. Again in the same file:
function beforeFilter()
{
$this->setAuth();
//any of the Auth constants you wish to overwrite
//on a Controller level
if($this->sessionAuth())
{
$this->__initAuth();
}
}
function beforeRender()
{
$this->__sessionAuth();
}
function __sessionAuth()
{
if ($this->Session->check(’Message’))
{
$auth_session = $this->Session->read('Message');
if (array_key_exists('auth', $auth_session) && array_key_exists('message', $auth_session['auth']))
{
//invalidating login form
$auth_flash = $auth_session['auth']['message'];
$userModel =& $this->Auth->getModel();
$userModel->invalidate($this->Auth->fields['username'], $auth_flash);
unset($auth_session['auth']);
$this->Session->write('Message', $auth_session);
return false;
}
}
return true;
}
function __setAuth()
{
if (isset($this->Auth))
{
/**
* The name of the component to use for Authorization or set this to
* ‘controller’ will validate Controller::action against Controller::isAuthorized(user, controller, action)
* ‘actions’ will validate Controller::action against an AclComponent::check()
* ‘crud’ will validate mapActions against an AclComponent::check()
* array(’model’=> ‘name’); will validate mapActions against model $name::isAuthorize(user, controller, mapAction)
* ‘object’ will validate Controller::action against object::isAuthorized(user, controller, action)
*
* @var string
* @access public
*/
$this->Auth->authorize = false;
/**
* The name of an optional view element to render when an Ajax request is made
* with an invalid or expired session
*
* @var string
*/
$this->Auth->ajaxLogin = null;
/**
* The name of the model that represents users which will be authenticated. Defaults to 'User'.
*
* @var string
*/
$this->Auth->userModel = 'User';
/**
* Additional query conditions to use when looking up and authenticating users,
* i.e. array('User.is_active' => 1).
*
* @var array
*/
$this->Auth->userScope = array();
/**
* Allows you to specify non-default login name and password fields used in
* $userModel, i.e. array('username' => 'login_name', 'password' => 'passwd').
*
* @var array
*/
$this->Auth->fields = array('username' => 'username', 'password' => 'passwd');
/**
* the hash function to use, options: sha1, sha256, md5
*
* @var string
*/
$this->Auth->hash = 'sha1';
/**
* The session key name where the record of the current user is stored. If
* unspecified, it will be "Auth.{$userModel name}".
*
* @var string
*/
$this->Auth->sessionKey = null;
/**
* If using action-based access control, this defines how the paths to action
* ACO nodes is computed. If, for example, all controller nodes are nested
* under an ACO node named 'Controllers', $actionPath should be set to
* "Controllers/".
*
* @var string
*/
$this->Auth->actionPath = null;
/**
* A URL (defined as a string or array) to the controller action that handles
* logins.
*
* @var mixed
*/
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
/**
* Normally, if a user is redirected to the $loginAction page, the location they
* were redirected from will be stored in the session so that they can be
* redirected back after a successful login. If this session value is not
* set, the user will be redirected to the page specified in $loginRedirect.
*
* @var mixed
*/
$this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'index');
/**
* The the default action to redirect to after the user is logged out. While AuthComponent does
* not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
* Defaults to AuthComponent::$loginAction.
*
* @var mixed
*/
$this->Auth->logoutRedirect = null;
/**
* The name of model or model object, or any other object has an isAuthorized method.
*
* @var string
*/
$this->Auth->object = null;
/**
* Error to display when user login fails. For security purposes, only one error is used for all
* login failures, so as not to expose information on why the login failed.
*
* @var string
*/
$this->Auth->loginError = 'Login failed. Invalid username or password.';
/**
* Error to display when user attempts to access an object or action to which they do not have
* acccess.
*
* @var string
*/
$this->Auth->authError = 'You are not authorized to access that location.';
/**
* Determines whether AuthComponent will automatically redirect and exit if login is successful.
*
* @var boolean
*/
$this->Auth->autoRedirect = true;
/**
* Controller actions for which user validation is not required.
*
* @var array
*/
$this->Auth->allowedActions = array();
}
}
function __initAuth()
{
if (isset($this->Auth))
{
$ignored_methods = array();
if ($this->authAllow == '*' || empty($this->authAllow))
{
$this->authAllow = am(array_keys($this->Auth->actionMap),
get_class_methods($this->__controllerName($this->name)));
$ignored_methods = am($ignored_methods,
get_class_methods($this->__controllerName('App')),
get_class_methods($this->__controllerName('')));
}
if (is_string($this->authAllow))
{
$this->authAllow = array($this->authAllow);
}
if (is_string($this->authDeny))
{
$this->authDeny = array($this->authDeny);
}
if (!empty($this->authDeny))
{
if (in_array('*', $this->authDeny))
{
$this->authDeny = am(array_keys($this->Auth->actionMap),
get_class_methods($this->__controllerName($this->name)));
}
$ignored_methods = am($ignored_methods, $this->authDeny);
}
if (!empty($this->authAllow))
{
foreach($this->authAllow as $method_name)
{
if (!in_array($method_name, $ignored_methods))
{
$this->Auth->allow($method_name);
}
}
}
}
}
function __controllerName($name)
{
return $name . ‘Controller’;
}
Examples
In our examples I will use CustomController with the following actions: customAction - anotherCustomAction - finalCustomAction. All the examples below are done by editing the /controllers/custom_controller.php file.- By default, allow access to all actions.
//do nothing special in your controller
//or you could use the wildcard var $authAllow = ‘*’;
//or even var $authAllow = array(’customAction’, ‘anotherCustomAction’, ‘finalCustomAction’); 2. Make only ‘anotherCustomAction’ accessible to everyone
var $authAllow = 'anotherCustomAction';3. You want to check authorization only for ‘finalCustomAction’, ‘create’, ‘delete’://or, just for the sake of showing the different possible ways var $authDeny = array(’customAction’, ‘finalCustomAction’);
//or, again just for showcasing the different methods
var $authDeny = array('finalCustomAction', 'create', 'delete');
4. You need to create a new beforeFilter() callback in CustomController:
//set authDeny or authAllow just like you would any of the above cases var $authAllow = 'customAction';Finally, to show the authorization’ error messages (if any) in your login view:function beforeFilter() { $this->setAuth(); //any changes you wish to bring to the default settings you made in AppController //should come here. //ie: if you want to use a different Model for authorization //or if you want to set an Auth->userScope for this specific controller $this->initAuth(); }
$session->flash('auth');
Notes
- Auth will just exit without doing anything if the referenced controller’s name is ‘TestsController’.
- Auth considers ‘login’ and ‘logout’ to be allowed methods, including them in Auth->allow() can make problems.
- All files should have no whitespace before the opening (
) tag and none after the closing (?>) tag. That will cause an error like:Warning (2): sessionstart() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at /path/to/app/controllers/customcontroller.php:2) [CORE/cake/libs/session.php, line 154]
Trackbacks
Use this link to trackback from your own site.


i want to see this in action.
now where are those
controllers options… im excited.
Auth->authorize=0; will disable acl checking or else you get error viewing pages.
I left ACL to default and all worked fine.
Hey Jad, thanks for the nice plug.
Yeah, my blog posting is more a “here’s how the options in Auth work” rather than a “here’s how to actually use Auth in a real application” posting. You’ve put together a nice practical tutorial here.
@Chris: thanks for dropping by. I can’t blame you for only scratching the surface with your post, after all you must be really busy developing the amazing framework we get to use, so props to that!
Look out for the next one where I will be uncovering all I discovered while working with the Auth and ACL components (different types of authorizations available, userScope, etc.) - not that you really need tutorials, but more because I care to hear your feedback.
@Jad
Hey, I still need tutorials from time to time…despite my involvement with CakePHP there are still areas that I am not 100% familiar with. I’m definitely a “learn by seeing a tutorial” type of person.
@Chris: I hear ya - am that kind too, tutorials and source digging. I tend to compare coding to driving, you can learn the basics and go with that or you can know your shit inside out and optimize it to suit you best - for a relatively big difference in performance and elegance.
[…] Loud Baking » Blog Archive » How to use AuthComponent in CakePHP A practical demonstration of the new Auth component for CakePHP 1.2 (tags: cakephp auth tutorial) […]
I want tutorials to get a nice kick-start. Learning how others do things is a great way to check the quality of your own work.
@Marc: I wanted to post it here but gwoo preferred I post on the bakery. I’ve been procrastinating now for a while, it’s all written, revised by him and everything, it’s only about posting it now; I should kick myself this week and finish with that. And I completely agree with you on the fact that checking what others are doing is a great way to learn/improve the way we do things ourself and sometimes, realize how good our solution is compared to other, either way it’s a plus. On a last note, thanks for dropping by!
Jad
Thanks, this works successfully! I see in auth.php that $hash is no present in the class, so I commented it.
replace $this->Auth->hash = ’sha1′; by Security::setHash(’sha1′);
This is exactly what I was looking for (having to set all the allowed actions seemed really tedious for a mostly public site). I did run into one small kink and was able to fix it.
Using the code supplied here my login form broke–it didn’t show error messages and the returning form came back auto-populating the hashed password field. Not cool. Making my login action an ignored method fixed this.
Replace:
$ignored_methods = array();
With:
$ignored_methods = array(’login’);
The form started working again and all is well.
I’m having trouble getting this to work 100%. The log in seems to work, it validates properly and redirects me to the page I specified (non-protected page). But then if I try to follow any links to protected pages I get a redirect loop (infinite redirect).
I write as you: Security::setHash(”md5″); but I got the following error: Invalid Login. this means the password was wrong, can you tell me the reason and contact me with email my email:mnbghj929.student@sina.com Thanks! my cakephp version:1.2…rc I come from china.Welcome you !
I’m new to cakephp and I’m trying to use the Auth component. Which is working fine at my project but I’m stucked with (I think) the most obvious thing of the world.
I want to show a diferent element (or a variation of the same element) depending if the user is logged or not. The only way I found to do it is by checking it in every function and setting a variable that I check on the view.
Is there any simpler way to do it ? Thanks in advance.