A Hopefully Useful Tutorial For Using CakePHP’s Auth Component

One of the parts of CakePHP that caused me the most grief while trying to learn the finer details of the framework was the Auth component. The problem at the time was that there was scant information available on how to actually use and configure it. Luckily, I was able to get some insider information via gwoo and Nate from the CakePHP core team. Plus there was stuff floating around on different web sites. So, here I will attempt to collect some of that information and show you some basic-to-intermediate info on how to use the component. Note, this code is for 1.2.x.x, and the API for the Auth component had not been declared 100% stable at the time of this post. In other words, don't blame me if this doesn't work 6 months from now. :)

So, in order to use this component, you have to add it to the list of components your controller is using. I tend to put this info in app_controller.php since I usually need Auth to work for all my controllers.

PHP:
  1. var $components = array('Auth');

So, now that we want to use it we suddenly have a huge variety of options that can and need to be set. For this example I use code from an existing project, with some info changed to protect the innocent. You set the various Auth options by putting code in your beforeFilter() method for your controller that will be using Auth.

PHP:
  1. $this->Auth->fields = array('username' => 'email', 'password' => 'pasword');

By default, Auth expects you to have a model called User that has a 'username' and 'password' field in it. You can override what fields contain the username and password information though.

PHP:
  1. $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');

You need to tell Auth what controller / action pair it needs to use to present the login form.

PHP:
  1. $this->Auth->loginRedirect = array('controller' => 'users', 'action' => 'index');

Tell the Auth component where the user should be redirected after a successful authentication...

PHP:
  1. $this->Auth->logoutRedirect = '/';

... and where they should go when you logout.

PHP:
  1. $this->Auth->loginError = 'Invalid e-mail / password combination.  Please try again';

You can specify what error message you want displayed if authentication fails.

PHP:
  1. $this->Auth->autoRedirect = false;

Sometimes you don't want to send an authenticated user to the location specified in $this->Auth->loginRedirect because you need to do some work based on the user being authenticated. I've used this in this bit of code that checks to see if we have a cookie that has user credentials in it for logging in.

PHP:
  1. function login() {
  2.         if ($this->Auth->user()) {
  3.             if (!empty($this->data)) {
  4.                 if (empty($this->data['User']['remember_me'])) {
  5.                     $this->Cookie->del('User');
  6.                 }
  7.                 else {
  8.                     $cookie = array();
  9.                     $cookie['email'] = $this->data['User']['email'];
  10.                     $cookie['token'] = $this->data['User']['pasword'];
  11.                     $this->Cookie->write('User', $cookie, true, '+2 weeks');
  12.                 }
  13.  
  14.                 unset($this->data['User']['remember_me']);
  15.             }
  16.  
  17.             $this->redirect($this->Auth->redirect());
  18.         }
  19.     }

PHP:
  1. $this->Auth->authorize = 'controller';

This is a newer addition to Auth, where you can specify some additional information about where Auth can look for more information. Normally, Auth will expect you to have already setup stuff using Cake's own ACL stuff. I didn't need anything as complicated as that, so I was happy to see I could bypass it. When you set $this->Auth->authorize to 'controller', you're telling the Auth component to look for an action called 'isAuthorized' for more information on whether the user is authorized or not. Here's a sample one:

PHP:
  1. function isAuthorized() {
  2.         if (isset($this->params[Configure::read('Routing.admin')])) {
  3.             if ($this->Auth->user('admin') == 0) {
  4.                 return false;
  5.             }
  6.         }
  7.        
  8.         return true;
  9.    }

I assume that example is self-explanatory. Now, if you set $this->Auth->authorize to 'model' then you can create a special method in your User model that can do similar things. Thanks to AcidMax for this post that shows you what to do if you use 'model' for $this->Auth->authorize. There is also a 'crud' option for $this->Auth->authorize, but I have not used it myself. You can use $this->Auth->mapActions to then tell auth what actions map to the various CRUD actions:

PHP:
  1. $this->Auth->mapActions(array('read'=> array('display')));

Like I said, I haven't fooled around with it. Once I do, I'll update this blog post to expand on it some more.

PHP:
  1. $this->Auth->allow('add', 'view');

You need to tell Auth what actions in your controller can be accessed without needing to be authorized. What trips most beginners up is that 'login' and 'logout' are already actions that Auth expects to not needing to be authorized. If you put 'login' and 'logout' in there, Auth starts acting very weirdly and totally bypassing authorization. So, don't do that. :)

Now, there are also some other features that are useful to those using Auth:

PHP:
  1. function logout() {
  2.         $this->Session->setFlash("You've successfully logged out.");
  3.         $this->redirect($this->Auth->logout());
  4.     }

$this->Auth->logout will return the URL to whatever location you told Auth you wanted people to be sent to upon them being logged out.

PHP:
  1. $userId = $this->Auth->user('id');

You can retrieve info from the User model if you've been successfully authorized. Whatever fields are in your User model can be retrieved. Wouldn't it be nice to do stuff like this:

PHP:
  1. if ($this->Auth->user('admin') == true) {
  2.      $this->Session->setFlash('You are an admin');
  3. }

Phew. That's a lot of stuff to try and keep in your head when building apps using Auth. But there's still more. The Auth component will automatically hash whatever value you place in the 'password' field in your form. It will do this both upon login *and* if you are creating a record in your user model, so you need to make sure that your field in your table that stores the password is large enough that it will accept the hashed password. If you need to do something with that password before you store it, call the field something other than what you've told Auth your password field is, then you can get the value needed to store it by using $this->Auth->password('passwordtostorehere').

Anyhow, I'm sure I've missed some other more esoteric stuff, but I hope I've done a good job of showing just how easy the Auth component really is to use...once you understand how it actually works. :)

29 Responses to this post.

  1. Matt's Gravatar

    Posted by Matt on 11.09.07 at 5:14 pm

    Great work Chris. I've been struggling with Auth for a few days now, determined to use the Cake 1.2 built in implementation thinking it would be the best long term.

    But after days of putting together bits and pieces from the bakery, Google group and various blogs I hadn't got very far at all.

    This morning I turned to obAuth and have it all running within an hour!

    For this project obAuth should do the job, but down the track I'd like to persist with Cake 1.2 Auth, and this tutorial is a good start.

    Thanks for your work.

  2. Kim Biesbjerg's Gravatar

    Posted by Kim Biesbjerg on 11.09.07 at 5:14 pm

    May want to change var $uses = array('Auth'); to $components = array('Auth') in your first code block. ;-)

  3. Tarique Sani's Gravatar

    Posted by Tarique Sani on 11.09.07 at 5:14 pm

    As I read the code $this->Auth->loginRedirect is used only if $this->Session->read('Auth.redirect') is not set or is same as the login action.

    Since the value of Auth.redirect is typically set to the url which required authentication in the first place - user is redirected to that page upon successful login.

  4. Markus Henke's Gravatar

    Posted by Markus Henke on 11.09.07 at 5:14 pm

    Great article as always!

    Auth and ACL are still on my list to learn. So your post hopefully helps me to understand Auth. Is it realy that simple as you describe it? Shame on me for not understanding it until now. :-(

  5. preloader's Gravatar

    Posted by preloader on 11.09.07 at 5:14 pm

    Thank you for that tutorial. Didn't work it through yet.

    Shouldn't it be

    var $components = array('Auth');

    instead of

    var $uses = array('Auth');

    ??

    Regards, Christoph

  6. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @kim, @preloader
    Thanks for pointing out the mistake. I've fixed it in the post

    @Tariq
    Not 100% sure about what you said, but I do know that you can set $this->Auth->loginRedirect to something else if you don't want people going automatically going to whatever page was covered by authorization. Sort of a "funnel" idea where you want everyone who logs in to end up at the same starting point.

  7. Aitch's Gravatar

    Posted by Aitch on 11.09.07 at 5:14 pm

    Great clear explanation, motivated me enough to try it out. I've followed this through and have it working to a point, I can get public pages, and redirect to login when required but my issue is that I don't store a "password" field in the model for my "Employee" - I have to authenticate against the corporate ldap directory, which I can do quite easily, but it's not clear how to do this in the context of Auth

    In this respect, (I think) I'm forced to use the manual Auth::login() method, but this and other methods scattered throughout the class assumes presence of a username/password combo (you can't bypass it changing Auth->fields either)

    I'm loathed to hack out all the password lines in the cake src, any thoughts on a suitable approach?

  8. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @Aitch
    You *might* be able to do what you're looking for my setting $this->Auth->authorize to 'model' and then creating a model that will grab info from your ldap directory. I was listening to a as-yet-to-be-released podcast with some of the CakePHP guys and one (phishy from #cakephp) mentioned he did something like that to authenticate against a RADIUS server. Go to the link I put in the post about using 'model' to find more details. Hope that helps.

  9. Sean’s Obsessions » Blog Archive » A simple authentication system with CakePHP 1.2 and Auth Component's Gravatar

    [...] my login function, I stole it directly from this tutorial. (Actually most of what I did was based on that tutorial, my contribution is really the explanation [...]

  10. foobar's Gravatar

    Posted by foobar on 11.09.07 at 5:14 pm

    Comment removed at poster's request

  11. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @foobar

    Um, call me old-fashioned but I believe that the error message tells you *exactly* what to do...

    (Hint: it involved changing something in app/config/core.php)

  12. Baz L's Gravatar

    Posted by Baz L on 11.09.07 at 5:14 pm

    I got a quick question. I don't see where you use the "remember me" cookie.

    Do you dump the values in the input boxes in the view?
    Or would you implement this in the isAuthorized() function?

    ThanX

  13. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @Baz L
    The 'remember me' cookie is used in another action elsewhere on the web site as part of a look up action. Basically I check for the cookie, pull the encoded credentials out and then compare them to what's in the database. Pretty simple.

  14. Stephen Orr's Gravatar

    Posted by Stephen Orr on 11.09.07 at 5:14 pm

    Chris, would you like to elaborate on how you use the remember me cookie? Maybe I'm being dumb, but I just can't figure out how to build a similar remember me feature for my own application.

    Also, if anyone is interested - I've extended the AuthComponent now to provide random salt generation, at the expense of a little loss of flexibility. I'm sure this could be refactored out, I was only interested in making it work for the time being. If there's interest, I'll stick it up in the PasteBin or somewhere.

  15. Follow-up to “A Hopefully Usefull Tutorial For Using CakePHP’s Auth Component” — @TheKeyboard's Gravatar

    [...] have been some mention in the comments for this post for clarification on the 'remember me' cookie that is mentioned in the code. I thought I'd [...]

  16. Tim Daldini's Gravatar

    Posted by Tim Daldini on 11.09.07 at 5:14 pm

    Where exactly in code does it hash the password before inserting the record. I have to call the auth password function in any case.

  17. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @Tim

    The Auth component actually handles that for you automatically, saving you the trouble of having to do it yourself.

  18. Aaron's Gravatar

    Posted by Aaron on 11.09.07 at 5:14 pm

    How does Auth hash password? I am trying your article out and I manually entered a user into my db but my password hash does not match. I used MD5 when I manually entered the record.

    Can you provide an example of registering/creating a user that works with this?

  19. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @Aaron
    The password hash is *not* MD5, it's whatever CakePHP is using as the default. Might be SHA1 or something. Dig around in Security::hash() to see what it's using.

    Unfortunately I can't provide a decent sample in the comments, but I'll put together a blog post about it.

  20. Aaron's Gravatar

    Posted by Aaron on 11.09.07 at 5:14 pm

    I found my answer on the hash method, but the register example would still be useful

  21. Tim Daldini's Gravatar

    Posted by Tim Daldini on 11.09.07 at 5:14 pm

    I found the solution to my problem as well. I was creating a User form to log in AND a Member form to register on -the same page/form-.

    Both when logging in and when adding a new member (which includes creating a member and an associated useraccount in my case) the password should be hashed.

    Obviously Auth only hashed the User login password but not the Member password. So I had to do this manually using the password function.

  22. Newbie's Gravatar

    Posted by Newbie on 11.09.07 at 5:14 pm

    As a newbie, I don't really understand where have to go all these piece of code. Could you provide a full working archive?

    Cheers

  23. Manuel's Gravatar

    Posted by Manuel on 11.09.07 at 5:14 pm

    Why:

    if ($this->Auth->user())

    in login() function? that will return null.

  24. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @Manuel

    If you're already logged in, it will return true...

  25. xmihu's Gravatar

    Posted by xmihu on 11.09.07 at 5:14 pm

    In a controller, if I do not need auth, how do?

    function beforeFilter() {
    parent::beforeFilter();
    if (isset($this->Auth)) {
    $this->Auth->authorize = false;
    }
    }

    it's not work!

  26. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 11.09.07 at 5:14 pm

    @xmihu

    If you don't want to use Auth in a controller, make sure that you are not adding it to your list of components to use or don't use the parent::beforeFilter() command if the parent controller is using Auth.

  27. Jeremy's Gravatar

    Posted by Jeremy on 11.09.07 at 5:14 pm

    I'm having trouble getting the cookie to be set. I've tried the same way it's entered here, and i've tried the way listen in the cookbook (basically same thing, but not using the array to list, and it's simply not making the cookie, anyone run into this? or know any common pitfalls?

  28. zsw's Gravatar

    Posted by zsw on 11.09.07 at 5:14 pm

    >>if ($this->Auth->user())

    >>in login() function? that will return null.

    I have the same problem: It has never return ture.

  29. Kien Pham's Gravatar

    Posted by Kien Pham on 11.09.07 at 5:14 pm

    @zsw: make sure your Configure::write('Session.start', true); is set to true in your /app/config/core.php file. I had the same problem, hope this help.

    @Christ: Thanks for a great post. Do we have a solution to authenticate against LDAP yet? I searched on the Bakery, they only have instructions on querying the LDAP, but not authenticate against it.

    Thanks,
    Kien

Respond to this post

Want to advertise on this blog? Send email to chartjes@littlehart.net
GTcars Canadian Car Audio TurboDodge Car For Sale Sign