Simple User Registration in CakePHP 1.2

In the comments for my seemingly-popular post about using CakePHP's Auth component (available in CakePHP 1.2), people have been having some questions about how the password is hashed and questions about a user registration system. Of course, the snarky response is "go and read the source for Security::Hash() and create some of your own code", but it is easier to just give people some code so they stop asking.

I'm in the process of building out an admin area for my simulation baseball league web site, and I created a registration system. Here's a condensed version of it.

First, I have my User model

PHP:
  1. <?php
  2. class User extends AppModel {
  3.  
  4.     var $name = 'User';
  5.     var $useTable = 'users';
  6. }

Next, I created my controller for my users and the registration action for it

PHP:
  1. class UsersController extends AppController {
  2.     var $name = 'Users';
  3.     var $helpers = array('Html', 'Form');
  4.     var $components = array('Auth');
  5.    
  6.     function beforeFilter() {
  7.         $this->Auth->allow('register');
  8.     }
  9.        
  10.     function register() {
  11.         if (!empty($this->data)) {
  12.             if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password_confirm'])) {
  13.                 $this->User->create();
  14.                 $this->User->save($this->data);
  15.                 $this->redirect(array('action' => 'index'));
  16.             }
  17.         }
  18.     }
  19. }

So, let's dissect this controller:

  • We're using the Auth component, set via "var $components = array('Auth')"
  • We tell Auth to not ask for authentication when doing the 'register' action
  • When we detect data coming into the 'register' action (usually via a POST), then check to see if the hashed password that Auth has created from the 'password' field in our form matches the hashed value of the 'confirm password' field from our form
  • If all that is okay, we create and save our new user record

Yes, it really is that easy.

The form for this is very simple as well

PHP:
  1. <?php
  2. echo $form->create('User', array('action' => 'register'));
  3. echo $form->input('username');
  4. echo $form->input('password');
  5. echo $form->input('password_confirm', array('type' => 'password'));
  6. echo $form->submit();
  7. echo $form->end();
  8. ?>

Throw all that stuff together and you now have a very simple user registration system.

Article Tags >> ||

28 Responses to this post.

  1. Davor Plehati's Gravatar

    Posted by Davor Plehati on 08.01.08 at 4:14 pm

    Hy,

    this is just what I need, a simple registration example. But I think that line 12. isn't right.

    if ($this->data['User']['password']==$this->Auth->password($this->data['User']['password_confirm']))

    how can you compare this two password values if the first value isn't a hash value and the second is?

  2. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Davor
    In this example, Tthe Auth component automatically hashes $this->data['User']['password'] for you, that's why it works. If I had changed the default password field to something else (some people use 'passwd' as the field name in their User model) then the Auth component would automatically hash $this->data['User']['passwd'] for you.

    Hope that helps.

  3. Davor Plehati's Gravatar

    Posted by Davor Plehati on 08.01.08 at 4:14 pm

    @Chris
    It helps, thanks.

    I have another question, about input validation. How would you validate if username is for example not less than 6 char and not more than 10 char ?
    I'm a bit confused about AppModel's variable $validate...

  4. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Davor
    Check out the section on 'Data Validation' over at http://tempdocs.cakephp.org

  5. Stephen Orr's Gravatar

    Posted by Stephen Orr on 08.01.08 at 4:14 pm

    In a related question to Davor's, how do you handle verifying the length of passwords when registering? Because the Auth component hashes the password, you can't then compare it with the password_confirm field?

  6. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Stephen
    It's quite easy to verify length under the system I proposed above. You can verify the length of password_confirm, and then do the comparison check in line 12 of my sample code. You do have to make an assumption on the length of the password because I guess there is an statistically improbable chance that two string of different lengths would be hashed the same.

  7. Gustavo Carreno's Gravatar

    Posted by Gustavo Carreno on 08.01.08 at 4:14 pm

    Hey Chris,

    Just a minor correction, sorry: you need the input field password to be array('type' => 'password') as the password_confirmation one on the form.

  8. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Gustavo
    Actually, you don't need to do that at all. The Form helper is smart enough to know that you want a 'password' field type when you create an input field called 'password'. The code up above is what I'm actually using in an application and the field does turn into a password field.

  9. francky06l's Gravatar

    Posted by francky06l on 08.01.08 at 4:14 pm

    Hi Chris,

    Just wonder if submitting empty passwords will work. I had this problem but quite a while ago since the Auth compoment's hashPasswords method would hash an empty field anyway (and a model validation for checking empty will never be hit). Maybe the component has been modified since.

    Cheers

  10. Matt Harrison's Gravatar

    Posted by Matt Harrison on 08.01.08 at 4:14 pm

    Hi,

    I've been puzzling over user registration for some time, although using my own Auth component. The problem I have, and one that it seems isn't often addressed, is outputting validation errors to the user.

    For example if the two passwords do not match, or invalid characters in the username. I know this should be taken care of by core validation but I cannot find an example anywhere.

  11. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Matt

    I have used model verification to handle this sort of thing, the key being that you need to have that second verification field for the passwords. I'll do up a blog post showing what I did.

  12. rogwei's Gravatar

    Posted by rogwei on 08.01.08 at 4:14 pm

    I do not see beforeFilter() in the api. Where is that defined?

  13. rogwei's Gravatar

    Posted by rogwei on 08.01.08 at 4:14 pm

    nevermind. I was looking at AppModel not AppController. forgive me.

  14. Tristan's Gravatar

    Posted by Tristan on 08.01.08 at 4:14 pm

    Hi,

    I am trying to get auth up and running as a first step...

    when i go to example.com/foo it redirects me to /users/login/ i enter my info, and i go to /foo

    in my fooController::index() i have $this->set('email', $this->Auth->user('email'));

    in my view i simply echo $email;

    instead of seeing my email i get a calling function on non-object crap... i followed the tempdoc.cakephp.org example...any idea what i could be doing wrong?

  15. Tristan's Gravatar

    Posted by Tristan on 08.01.08 at 4:14 pm

    Ok...so it appears that despite Auth kinda working, you have to ensure that var $components = array('Auth'); and not var $components = array('auth');

  16. jen's Gravatar

    Posted by jen on 08.01.08 at 4:14 pm

    How does Auth know that register belongs to it (to hash the password)? Because, it's not hashing it for me.

    I am using a table that is not called "users". That's not supposed to be a problem in theory - in practice, is anyone doing this?

  17. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Jen

    If you tell the Auth component the name of your table that you're using, then it should automatically hash the password field for you.

  18. jen's Gravatar

    Posted by jen on 08.01.08 at 4:14 pm

    Thanks, I had a dumb typo. Working now.

    Great tutorials, thanks!

  19. Ryan Waggoner's Gravatar

    Posted by Ryan Waggoner on 08.01.08 at 4:14 pm

    How would you have the register() method automatically log the user in once the data has been validated and saved? I'm trying to avoid having the user immediately need to login after a successful registration.

    Great tutorials...thanks!

  20. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @Ryan

    I believe you can manually log someone in by placing their login credentials into an array and then passing it to the login method of the component. But you have to have their non-hashed password stored somewhere in order to make it happen

    $password = $this->Auth->password($nonHashedPassword);
    $data = array('username' => 'foo', 'password' => $password);
    $this->Auth->login($data);

  21. Mickey A's Gravatar

    Posted by Mickey A on 08.01.08 at 4:14 pm

    Thanks for the great tutorial :). I followed all your instruction and everything went fine and dandy except one thing. I ran into this problem: when the registration process fails by any reason (passwords do not match, repeated login, etc), the form reloads itself with the error code, but for some reason, my password field isn't being empty but filled with the hashed password that generated from the system. I tried a couple option attribute like array('empty'=>true) or change from $form->input('password') to $form->password(...) but none works for me.

    Any help or suggestion will be greatly appreciated!
    Thanks,

  22. Kepten's Gravatar

    Posted by Kepten on 08.01.08 at 4:14 pm

    use echo $form->input('new_password', array('value' => ''));

  23. in-prompt's Gravatar

    Posted by in-prompt on 08.01.08 at 4:14 pm

    AS another user mentioned the auth component will hash even blank passwords which is a basic validation rule, this problem could be addressed using other JS validation (you should have double validation, client and server) .
    Now to validate on the server side dont use the standard password field name and then hash the password yourself on beforesave() , make sure u use the salt. sorry cant go into detail i have no time ! good luck. ps: consider another mature framework

  24. Erik Gyepes's Gravatar

    Posted by Erik Gyepes on 08.01.08 at 4:14 pm

    Nice articl, but what if the passwords are not equal? How will I display an error message? Should I invalidate that field in the else statement or display and error with setFlash? What do you recommend?

  25. elvman's Gravatar

    Posted by elvman on 08.01.08 at 4:14 pm

    Is this example safe against hacks? I mean if a user adds a hidden field named 'id' on his browser, wouldn't your example edit the existing user? I think that cake will search for the user with the given id and change it's password instead of creating a new one.

  26. Chris Hartjes's Gravatar

    Posted by Chris Hartjes on 08.01.08 at 4:14 pm

    @elvman

    That example is just that: a simple example. If you read up on the using the Security component, I think you'll find info in there on how to prevent the exact thing you're talking about. This is, of course, without me going and looking for myself. ;)

  27. david's Gravatar

    Posted by david on 08.01.08 at 4:14 pm

    Shouldn't the form fields in the register.ctp file be in the form:

    $form->input('User/username');
    $form->input('User/password');
    $form->input('User/password_confirm'...)

    Otherwise the $this->data['User'][field_name] references in the controller won't be correct .. ?

  28. Dave's Gravatar

    Posted by Dave on 08.01.08 at 4:14 pm

    Follow-up question.

    My registration form includes 'User' information as well as 'Address' information.
    These two database tables are associated. (User hasOne Address).

    How do I save BOTH the User object AND the Address object when I click the submit button?
    In other words, when saving the User, how do I also save the associated Address? Help me setup the form.

    Thanks,
    Dave

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
Audi Forum Mustang Forum Dodge Intrepid Miata Turbo
GTscene Pontiac Bonneville


@TheKeyboard is Digg proof thanks to caching by WP Super Cache!