Logging in Users using Doctrine and Zend_Auth

January 26th, 2010

Here’s the second part of my Doctrine / Zend_Auth example. In 15 minutes, we create a logout, login and protected area that’s reliant on the ZC_Auth_Adapter adapter we created in last week’s video. Notice how there’s no code in the IndexController exposing the authentication implementation,

Grab a copy of the project or browse the repository.

Also, a big thank you to ServerGrove for extending their support of Zendcasts for January. ServerGrove specializes in Zend Framework hosting and they’ve offered a 10% rebate on hosting with coupon code “zc”. If you’re looking for a host, be sure to check them out (referral). They’ve also added an additional coupon for “Mini Hosting” plans, get $2 off by using code “zcmini”.

 

discuss video in the forum

25 Responses to “Logging in Users using Doctrine and Zend_Auth”

  1. Danny says:

    Nice & easy implementation

  2. BEREGU says:

    Very cool…. ^^

  3. David says:

    Thanks for the clear and straight forward tutorials.

    I find login is required for most controllers. For these cases the hasIdentity check and redirect can be moved from each action to a single instance in the predispatch function for that controller.

    For instance:

    public function preDispatch()
    {
    if (!$this->Zend_Auth::getInstance()->hasIdentity()) {
    $this->_redirect(‘/’);
    }
    }

    Of course it wouldn’t work so well for the controller that actually handles login and needs the unauthenticated users to reach some page to enter there credentials.

  4. milan l. says:

    Hi Jon,
    first off all I want to say a huge thanks for zendcasts!!

    And I have a small question for you. Is there any way how to set logging in this way: “Every time doctrine connects to the database write how long that request took.” I mean is it possible to set this thing globaly?

    thanks for answer.. Milan

  5. jon says:

    hey Milan,

    I haven’t experimented with this personally, but I would look into Doctrine’s plugin architecture and specifically it’s profiler:
    http://www.doctrine-project.org/documentation/manual/1_0/hu/component-overview:profiler

  6. jon says:

    Hey David, you’re quite right about the authentication being in the wrong place! In a typical application, I would have the login code redirect to a different module in my app that’s protected. I would then use a Controller_Plugin to check the module and compare the module with the Zend_Auth identity to see if the person is allowed to access the module. This example strips out those layers in favour of showing the simplicity of the actual authentication code.

  7. Rhys says:

    I’ve just finished building my first ap with zend and wish I’d found your site before I started building it. I reckon I’ll go through the videos from the first one you made onwards to try and learn zend properly.

    Cheers

  8. marsbomber says:

    Most of the time, I found myself needing authentication on a controller by controller basis. so my approach is to write myself a custom MyApp_Controller_Action class by extending the default Zend_Controller_Action. In this custom controller action class, I basically do the Zend_Auth check and other common things that you may want to do for your authentication protected controllers. Then I’ll have my auth required controllers extend MyApp_Controller_Action, and those public controllers (ie, login, index, etc) extend the default Zend_Action_Controller.

    Not sure if this is a good approach, but there you go.

  9. martin says:

    Hi, a small question. I implement this in my project but using this when the user has identity in my Controller
    if(Zend_Auth::getInstance()->hasIdentity()) {
    //$this->_forward(‘access’);
    //Instead of use forward I want to use
    $this->_redirect(‘/admin/access’);
    //to redirect to another
    //module/controller

    }

    but when I use the code above I get this error:
    Zend_Session::start() – /opt/lampp/htdocs/library_1.9/Zend/Loader.php(Line:164): Error #2 fopen(/opt/lampp/htdocs/transpanish.biz/application/models/AdministradorTable.php) [function.fopen]: failed to open stream: No existe el fichero รณ directorio Array

    It seems that it want to write on AdministradorTable.php This file doesn’t exist only Administrador.php in my Models directory. What is happening ?
    Thank you and great videos I am learning a lot.

  10. jon says:

    Hi martin,
    I would double check your Doctrine configuration. It sounds like for some reason there’s been a flag set for generating table classes in Doctrine in addition to the models. You’ll want to disable that and regenerate the base classes. You can also compare your configuration with the one on zendcasts’ google code site.

  11. martin says:

    Hi Jon, everything seem to be ok, but I saw when I comment this line in Zend_Auth::getInstance()->clearIdentity(); in my logout method redirection take place without problems. The same when use if I comment (!Zend_Auth::getInstance()->hasIdentity())
    to controll access in my controller. Zend_Auth is trying to write something in a wrong place. I am using routers so maybe the problem is there. I ‘ll check it. Thank you.

  12. dave says:

    Hi Joe, I followed your instructions step by step, but I also received the error which is similar to martin’s when trying to execute “Zend_Auth::getInstance()->clearIdentity()” and “!Zend_Auth::getInstance()->hasIdentity()”

    The error says:

    Message: Zend_Session::start() – D:\Program Files\WAMP\www\zf\library\Zend\Loader.php(Line:181): Error #2 fopen(D:\Program Files\WAMP\www\zf\application/models/UserTable.php) [function.fopen]: failed to open stream: No such file or directory

    Has anyone found a solution?

  13. Understack says:

    Hi Jon,

    Thanks for putting up wonderful tutorials.

    I;ve got 2 questions:

    1. I’ve noticed that you said Doctrine 2.0 would support multiple ‘modules’ (default/admin etc). Doctrine 2.0 Alpha has been released. Can I expect this feature in alpha release. Are you planning for a tutorial on that :) ?

    2. How can I use 2 databases with Doctrine?

    Thanks.

  14. jon says:

    Hi there,
    1. I’m going to wait till the dust settles and an “official” approach to doing this is published.
    2. I would look at Doctrine’s connection documentation: http://www.doctrine-project.org/documentation/manual/1_1/e+n/connections

    Hope that helps!

  15. martin says:

    Hi Dave. I couldn’t solve this. It seems that Zend_Auth loose their data across the pages. I don’t know how to storage this data like I have been using with Zend_Auth_Adapter_DbTable because those methods doesn’t exist for Zend_Auth_Adapter_Interface or I have been doing bad calls of this methods :( I use this approach (http://blog.elinkmedia.net.au/2010/01/24/zf-authentication-using-doctrine/comment-page-1/#comment-40) and it works ok, maybe you could make a plugin to use it like Jon’s did in his tutorial.

  16. marsbomber says:

    @martin and @dave, if you are interested, I made another post, which shows how I introduced a auth service class to take away the auth logic from the controller. you may want to checkout.

    I’ll try to give Jon’s sample app a shot later today and let you know if I have similar issues.

  17. marsbomber says:

    @jon I downloaded the sample app, and tried to make a _redirect to a different controller after auth. for some reason, it seems like the identity does not persist. Also, there’s an error in the schema yaml file from the goolecode. i think the last line should be “name: string()” instead.

  18. jon says:

    @marsbomber Good catch! I got my YAML files confused.

    in terms of the _redirect, you can also try the redirector: http://framework.zend.com/manual/en/zend.controller.actionhelpers.html#zend.controller.actionhelpers.redirector

  19. marsbomber says:

    @jon tried the redirector in indexcontroller, $this->_redirector->gotoUrl(‘/abc’);
    in my abc controller,
    if (!Zend_Auth::getInstance()->hasIdentity()) {
    var_dump(Zend_Auth::getInstance()->getIdentity()->toArray());
    }
    else {
    echo “NO GOOD”;
    }

    i still have no good printed.

  20. dave says:

    @martin, I downloaded the source code and compared each file, and the cause of the issue I found was actually in the bootstrap file:

    $manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true);

    As soon as I removed the line above, it didn’t give me any error of Zend_Sessions.

    Hope it helps.

  21. martin says:

    Genius @dave !! That’s right, I probe it and everything is ok and I realized that @jon’s googlecode does not appear that line, I don’t know where I got it. Everything ok, big thanks for you, @jon and @jim, greetings.

  22. Whisher says:

    Hi,
    As usual excellent tutorial :)
    Do you think is it the right approach
    to get auth either with username or with email
    public static function authenticate($usernameOrEmail, $password)
    {
    try{
    if(false !== filter_var($usernameOrEmail, FILTER_VALIDATE_EMAIL)){
    $user = self::findOneByEmail($usernameOrEmail);
    }
    else{
    $user = self::findOneByUsername($usernameOrEmail);
    }
    }
    catch(Exception $e){
    throw new Exception(self::DB_TROUBLES);
    }
    if (false !== $user){
    if ($user->password == $password){
    if($user->status === ‘1′){
    return $user;
    }
    throw new Exception(self::WRONG_STATUS);
    }
    throw new Exception(self::WRONG_PW);
    }
    throw new Exception(self::NOT_FOUND);
    }

    Bye.
    PS

    My 5 cents to avoid storing the password
    in session
    public function authenticate()
    {
    try
    {
    $this->user = Model_User::authenticate($this->usernameOrEmail, $this->password);
    }
    catch (Exception $e)
    {
    if ($e->getMessage() == Model_User::DB_TROUBLES){
    return $this->result(Zend_Auth_Result::FAILURE, self::DB_TROUBLES_MESSAGE);
    }
    if ($e->getMessage() == Model_User::NOT_FOUND){
    return $this->result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, self::NOT_FOUND_MESSAGE);
    }
    if ($e->getMessage() == Model_User::WRONG_PW){
    return $this->result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, self::BAD_PW_MESSAGE);
    }
    if ($e->getMessage() == Model_User::WRONG_STATUS){
    return $this->result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, self::BAD_STATUS_MESSAGE);
    }
    }
    $this->user->password = null;
    return $this->result(Zend_Auth_Result::SUCCESS);
    }

    You never know :)

  23. shaded says:

    Great set, i was finally about to merge the series on zend_db, zend_acl and zend_auth to come up with a decent demo:)

    How do you get your var_dump output to be formatted so nicely?

     tags help, but still not as nice as yours.
  24. Rik says:

    @shaded:
    I could be wrong, but i think that formatting is the xdebug extension doing it’s thing.

    I’ve got xdebug installed and when I do a var_dump my output gets automatically formatted. Just like a stack trace when an exception has occurred.

  25. JohnB says:

    If you are using apc chache on top of public/index.php you have to put:

    function shutdown()
    {
    Zend_Session::writeClose(true);
    }
    register_shutdown_function(’shutdown’);

    otherwise it’ll crash.
    here is more about that:
    http://stackoverflow.com/questions/1364750/opcode-apc-xcache-zend-doctrine-and-autoloaders

Leave a Reply

Desktop RSS feed iPhone + iPod