Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Working with AuthenticationService and Session Db Save Handler

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on May 29, 2013

zf2-zendframework2One of the Session SaveHandlers that Zend Framework 2 provide is DbTableGateway Save Handler that utilize Zend\Db as a session save handler. How to combine authentication service with it ? Let’s learn about it! Save Handlers themselves are decoupled from PHP’s save handler functions and are only implemented as a PHP save handler when utilized in conjunction with Zend\Session\SessionManager.
1. Preparation
a. create tables

-- table to save session data....
CREATE TABLE IF NOT EXISTS `session` (
  `id` char(32) NOT NULL DEFAULT '',
  `name` char(32) NOT NULL DEFAULT '',
  `modified` int(11) DEFAULT NULL,
  `lifetime` int(11) DEFAULT NULL,
  `data` text,
  PRIMARY KEY (`id`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- users table
CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) NOT NULL,
  `password` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

-- users data with password = md5('admin')
INSERT INTO `users` (`id`, `username`, `password`) VALUES
(1, 'admin', '21232f297a57a5a743894a0e4a801fc3');

b. create a module with structure like the following ( don’t judge me I’m not explain file location again :p )

sessionsavehandler

2. Create Classes
a. AuthStorage
It’s a class that extends Zend\Authentication\Storage\Session and utilize SessionManager to set Db Handler.

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Storage/AuthStorage.php
namespace SanAuthWithDbSaveHandler\Storage;

use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Authentication\Storage;
use Zend\Session\Config\SessionConfig;
use Zend\Db\TableGateway\TableGateway;
use Zend\Session\SaveHandler\DbTableGateway;
use Zend\Session\SaveHandler\DbTableGatewayOptions;

class AuthStorage extends Storage\Session
    implements ServiceLocatorAwareInterface
{
    protected $serviceLocator;
    protected $namespace;

    public function __construct($namespace = null)
    {
        parent::__construct($namespace); 

        $this->namespace = $namespace;
    }
    
    public function setDbHandler()
    {
        $tableGateway = new TableGateway('session', 
                                            $this->getServiceLocator()
                                                 ->get('Zend\Db\Adapter\Adapter'));

        $saveHandler = new DbTableGateway($tableGateway,
                                            new DbTableGatewayOptions());
        
        //open session
        $sessionConfig = new SessionConfig();
        $saveHandler
            ->open($sessionConfig->getOption('save_path'), $this->namespace);
       //set save handler with configured session 
       $this->session->getManager()->setSaveHandler($saveHandler);
    }

    public function write($contents)
    {
        parent::write($contents);
        /**
            when $this->authService->authenticate(); is valid, the session 
            automatically called write('username')
          in this case, i want to save data like
         ["storage"] => array(4) {
              ["id"] => string(1) "1"
              ["username"] => string(5) "admin"
              ["ip_address"] => string(9) "127.0.0.1"
              ["user_agent"] => string(81) "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7;
                rv:18.0) 
              Gecko/20100101    Firefox/18.0"
        }*/
        if (is_array($contents) && !empty($contents)) {
            $this->getSessionManager()
                          ->getSaveHandler()
                          ->write($this->getSessionId(), \Zend\Json\Json::encode($contents));
        }
    }

    public function clear()
    {
        $this->getSessionManager()->getSaveHandler()->destroy($this->getSessionId());
        parent::clear();
    }

    public function getSessionManager()
    {
        return $this->session->getManager();
    } 

    public function getSessionId()
    {
        return $this->session->getManager()->getId();
    }

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }

    public function getServiceLocator()
    {
        return $this->serviceLocator;
    }
}

b. AuthStorageFactory
Remember, that setting up service via closure is bad for performance, so we need to make a factory class for it.

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Factory/Storage/AuthStorageFactory.php
namespace SanAuthWithDbSaveHandler\Factory\Storage;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use SanAuthWithDbSaveHandler\Storage\AuthStorage;

class AuthStorageFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $storage = new AuthStorage('my_storage_namespace');
        $storage->setServiceLocator($serviceLocator);
        $storage->setDbHandler();
        
        return $storage;
    }
}

c. AuthenticationServiceFactory
This is a Class that call AuthStorage class.

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Factory/Storage/AuthenticationServiceFactory.php
namespace SanAuthWithDbSaveHandler\Factory\Storage;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Adapter\DbTable as DbTableAuthAdapter;

class AuthenticationServiceFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $dbAdapter      	 = $serviceLocator->get('Zend\Db\Adapter\Adapter');
        $dbTableAuthAdapter      = new DbTableAuthAdapter($dbAdapter, 'users',
                                                  'username','password', 'MD5(?)');
        
        $authService = new AuthenticationService($serviceLocator->get('AuthStorage'),
                                                                $dbTableAuthAdapter);
        
        return $authService;
    }
}

d. LoginForm class

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Form/LoginForm.php
namespace SanAuthWithDbSaveHandler\Form;

use Zend\Form\Form;
use Zend\InputFilter;

class LoginForm extends Form
{
    public function __construct()
    {
        parent::__construct();
         
        $this->setAttribute('method', 'post');
        
        $this->add(array(
            'name' => 'username',
            'type' => 'Text',
            'options' => array(
                'label' => 'Username : '
            ),
        ));
        
        $this->add(array(
            'name' => 'password',
            'type' => 'Password',
            'options' => array(
                'label' => 'Password : '
            ),
        ));
        
         $this->add(array(
            'name' => 'Loginsubmit',
            'type' => 'Submit',
            'attributes' => array(
                'value' => 'Login',
                'id' => 'Loginsubmit',
            ),
        ));
         
        $this->setInputFilter($this->createInputFilter());
    }
    
    public function createInputFilter()
    {
        $inputFilter = new InputFilter\InputFilter();

        //username
        $username = new InputFilter\Input('username');
        $username->setRequired(true);
        $inputFilter->add($username);
        
        //password
        $password = new InputFilter\Input('password');
        $password->setRequired(true);
        $inputFilter->add($password);

        return $inputFilter;
    }
}

e. AuthController
This is a controller that place a Login Form and authentication service.

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Controller/AuthController.php
namespace SanAuthWithDbSaveHandler\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\Authentication\AuthenticationService;
use Zend\View\Model\ViewModel;

class AuthController extends AbstractActionController
{
    protected $authService;
    
    //we will inject authService via factory
    public function __construct(AuthenticationService $authService)
    {
        $this->authService = $authService;
    }
    
    public function indexAction()
    {
        if ($this->authService->getStorage()->getSessionManager()
                 ->getSaveHandler()
                 ->read($this->authService->getStorage()->getSessionId())) {
            //redirect to success controller...
            return $this->redirect()->toRoute('success');
        }
        
        $form = $this->getServiceLocator()
                     ->get('FormElementManager')
                     ->get('SanAuthWithDbSaveHandler\Form\LoginForm');   
        $viewModel = new ViewModel();
        
        //initialize error...
        $viewModel->setVariable('error', '');
        //authentication block...
        $this->authenticate($form, $viewModel);
        
        $viewModel->setVariable('form', $form);
        return $viewModel;
    }
    
    /** this function called by indexAction to reduce complexity of function */
    protected function authenticate($form, $viewModel)
    {
        $request = $this->getRequest();
        if ($request->isPost()) {
            $form->setData($request->getPost());
            if ($form->isValid()) {
                $dataform = $form->getData();
                
                $this->authService->getAdapter()
                                       ->setIdentity($dataform['username'])
                                       ->setCredential($dataform['password']);
                $result = $this->authService->authenticate();
                if ($result->isValid()) {
                    //authentication success
                    $resultRow = $this->authService->getAdapter()->getResultRowObject();
                   
                    $this->authService->getStorage()->write(
                         array('id'          => $resultRow->id,
                                'username'   => $dataform['username'],
                                'ip_address' => $this->getRequest()->getServer('REMOTE_ADDR'),
                                'user_agent'    => $request->getServer('HTTP_USER_AGENT'))
                    );
                    
                    return $this->redirect()->toRoute('success', array('action' => 'index'));;       
                } else {
                    $viewModel->setVariable('error', 'Login Error');
                }
            }
        }
    }
    
    public function logoutAction()
    {
        $this->authService->getStorage()->clear();
        return $this->redirect()->toRoute('auth');
    }
}

f. AuthControllerServiceFactory
Controller creation need a factory, so we need to create a factory for it.

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Factory/Controller/AuthControllerServiceFactory.php
namespace SanAuthWithDbSaveHandler\Factory\Controller;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use SanAuthWithDbSaveHandler\Controller\AuthController;

class AuthControllerServiceFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $authService = $serviceLocator->getServiceLocator()->get('AuthService');
        $controller = new AuthController($authService);
        
        return $controller;
    }
}

g. SuccessController
it’s a page that be redirected from AuthController. I’ve made a test only, you can check in real application. Real application should has authService that injected by EventManger in Module::onBootstrap.

//filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Controller/SuccessController.php
namespace SanAuthWithDbSaveHandler\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\Authentication\AuthenticationService;
use Zend\View\Model\ViewModel;

class SuccessController extends AbstractActionController
{   
    public function indexAction()
    {
        //here for test only, you should check session
        //for real application       
    }
}

h. login page

<?php
//filename : module/SanAuthWithDbSaveHandler/view/san-auth-with-db-save-handler/auth/index.phtml
$title = 'Login';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>

<?php
$form = $this->form;
$form->setAttribute('action', $this->url(
    'auth'
));
$form->prepare();

echo $this->form()->openTag($form);
echo $this->formCollection($form);
echo $this->form()->closeTag();

echo $this->error;

i. success page

<?php //filename : module/SanAuthWithDbSaveHandler/view/san-auth-with-db-save-handler/success/index.phtml ?>

login succeded.
<a href="<?php echo $this->url('auth', array('action' => 'logout')); ?>">Logout</a>

3. Register services
We should register services at SanAuthWithDbSaveHandler/config/module.config.php

//filename : SanAuthWithDbSaveHandler/config/module.config.php
namespace SanAuthWithDbSaveHandler;

return array(
    
    //controllers services...
    'controllers' => array(
        'factories' => array(
            'SanAuthWithDbSaveHandler\Controller\Auth' =>
               'SanAuthWithDbSaveHandler\Factory\Controller\AuthControllerServiceFactory'  
        ),
        'invokables' => array(
            'SanAuthWithDbSaveHandler\Controller\Success' =>
               'SanAuthWithDbSaveHandler\Controller\SuccessController'   
        ),
    ),
    
    //register auth services...
    'service_manager' => array(
        'factories' => array(
            'AuthStorage' =>
               'SanAuthWithDbSaveHandler\Factory\Storage\AuthStorageFactory',
            'AuthService' => 
               'SanAuthWithDbSaveHandler\Factory\Storage\AuthenticationServiceFactory',
        ),
    ),
    
    //routing configuration...    
    'router' => array(
        'routes' => array(
            
            'auth' => array(
                'type'    => 'segment',
                'options' => array(
                    'route'    => '/auth[/:action]',
                    'defaults' => array(
                        'controller' => 'SanAuthWithDbSaveHandler\Controller\Auth',
                        'action'     => 'index',
                    ),
                ),
            ),
            
            'success' => array(
                'type'    => 'segment',
                'options' => array(
                    'route'    => '/success[/:action]',
                    'defaults' => array(
                        'controller' => 'SanAuthWithDbSaveHandler\Controller\Success',
                        'action'     => 'index',
                    ),
                ),
            ),
        ),
    ),
    
    //setting up view_manager
    'view_manager' => array(
        'template_path_stack' => array(
            'SanAuthWithDbSaveHandler' => __DIR__ . '/../view',
        ),
    ),
);

4. Last but not least, a Module Class ! (It’s very important! haha :D )

//filename : SanAuthWithDbSaveHandler/Module.php
namespace SanAuthWithDbSaveHandler;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . str_replace('\\', '/' , __NAMESPACE__),
                ),
            ),
        );
    }
}

Now, try to login to application with url : http://YourZF2app/auth with username : admin and password : admin.

Done !
btw, I’ve uploaded source to my github account : https://github.com/samsonasik/SanAuthWithDbSaveHandler

References :
1. https://zf2.readthedocs.org/en/latest/modules/zend.session.save-handler.html
2. https://zf2.readthedocs.org/en/latest/modules/zend.authentication.intro.html
3. https://github.com/cgmartin/ZF2FileUploadExamples/blob/master/src/ZF2FileUploadExamples/Form/CollectionUpload.php

About these ads

85 Responses

Subscribe to comments with RSS.

  1. totcha said, on May 30, 2013 at 2:23 am

    Fine post. I’ve been wrapping my head around ZF2 auth/acl/sessions components, so this helps a lot. And can’t thank you enough for your other posts on ZF2 — as there’s still not enough docs available on the framework’s website. I hope you’ll keep sharing with us and best of luck for you!

  2. Bakyt Niyazov said, on June 2, 2013 at 1:23 pm

    Very useful post, as always! Thank you very much!

    I have a question though. In my case system is now using two handlers. Native one and this one. For example if I manually delete the session row from table, my users are still staying logged in because php is using it’s default handler. If I remove the session file, users log out.

    Do you know why it’s that?

    • Bakyt Niyazov said, on June 2, 2013 at 5:27 pm

      oh I got it now. The topic is not about setting default session handler it’s about auth service and session db save handler. So my question is wrong. P.S: I successfully resolved my issue by myself!

  3. Alexander Taran JR. said, on June 3, 2013 at 6:52 pm

    Hello samsonasik! I hasten to say a big thank you for every article you’ve written about Zend Framework 2 work you have done. Each topic has helped me understand the structure and arrangement of the ZF2. But I came across a problem at what point of the code, to check whether the user is authenticated, and if not, redirect him to a mandatory authentication. Even if the resource does not exist.

    Alex Taran, with Best Regards.

    • samsonasik said, on June 4, 2013 at 8:41 am
              if ( ! $this->getServiceLocator()->get('AuthService')->getStorage()->getSessionManager()
                       ->getSaveHandler()
                       ->read($this->getServiceLocator()->get('AuthService')->getStorage()->getSessionId())) {
                  //redirect to success controller...
                  return $this->redirect()->toRoute('auth');
              }
      
      • Alexander Taran JR said, on June 4, 2013 at 3:04 pm

        Hello again, I understanding how to check this one, but i didn’t know how to attach this check in Module.php file. I tried to attach some function in onBootstrap but i everytime have a problem with with error: could not find ServiceLocator.

      • samsonasik said, on June 4, 2013 at 4:23 pm
        public function onBootstrap(MvcEvent $e)
        {
                $em = $e->getApplication()->getEventManager();
                $em->attach('route', array($this, 'checkSession'));
        }
        
        public function checkSession(MvcEvent $e)
        {
                $sm = $e->getApplication()->getServiceManager();
              if ( ! $sm->get('AuthService')->getStorage()->getSessionManager()
                 ->getSaveHandler()
                 ->read($sm->get('AuthService')->getStorage()->getSessionId())) {
                         $controller = $e->getRouteMatch()->getParam('controller');
                        if ($controller != 'Auth') {
                        return $e->getTarget()->getEventManager()->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e)  {
                            $controller = $e->getTarget();
                            $controller->redirect()->toRoute('auth');
                        }, -11);
                    }   
               }        
        }
        
  4. Alexander Taran JR said, on June 5, 2013 at 3:08 am

    You did the right thing! I do not want to authenticated users logged in without seeing a 404 error, even if the page does not exist. So I’m a little cut your code. Thank you very much for the great help!

    public function checkSession(MvcEvent $e)
    {
    $sm = $e->getApplication()->getServiceManager();
    if ( ! $sm->get(‘AuthService’)->getStorage()->getSessionManager()
    ->getSaveHandler()
    ->read($sm->get(‘AuthService’)->getStorage()->getSessionId())) {
    if ($e->getRouteMatch()->getParam(‘controller’) != ‘SanAuthWithDbSaveHandler\Controller\Auth’) {
    $e->getRouteMatch()->setParam(‘controller’, ‘SanAuthWithDbSaveHandler\Controller\Auth’);
    }
    }
    }

  5. Bba said, on June 7, 2013 at 10:38 am

    Hi
    Great tutorial
    Any ideas how to unserialize the data in your sessions table
    Thanks

    • samsonasik said, on June 8, 2013 at 2:03 am
              $user = \Zend\Json\Json::decode( $this->getServiceLocator()->get('AuthService')->getStorage()->getSessionManager()
                              ->getSaveHandler()->read($this->getServiceLocator()->get('AuthService')->getStorage()->getSessionId()), true);
      
  6. Ali said, on June 20, 2013 at 4:46 pm

    Salam Alaykom . I want to thank you for you’r great Blog it was very helpful for me as begenner on ZF2 , i started to create my project and i used this authentification module that you create , just i whant to add some user on the “Users” table but when i try to login with other user that i create i had alwas alwas wrong password message . I thinck that the probleme’s the convetion to MD5 on the password row on My data base . i know that the solution’s simple but i m really blocked her . Great thanks

    • samsonasik said, on June 20, 2013 at 4:51 pm

      try to insert with :

      insert into users(user_name, pass_word) values('user', md5('user'));
      

      and try to login with :
      user_name : user
      pass_word : user

      • Ali said, on June 20, 2013 at 9:26 pm

        thx It works with Mysgl , But when i try to insert from my console application the passwrod’s not converted on MD5 format on database and i can’t login with the username and password that create .

      • samsonasik said, on June 24, 2013 at 5:17 am

        that’s your console problem, try to place script at file, at call that within mysql command.

      • Ali said, on June 20, 2013 at 9:30 pm

        This’s rhe Form

        setAttribute(‘method’, ‘post’);
        $this->add(array(
        ‘name’ => ‘id’,
        ‘type’ => ‘Hidden’,
        ));
        $this->add(array(
        ‘name’ => ‘username’,
        ‘type’ => ‘Text’,
        ‘options’ => array(
        ‘label’ => ‘Nom’,
        ),
        ));
        $this->add(array(
        ‘name’ => ‘password’,
        ‘type’ => ‘Password’,
        ‘options’ => array(
        ‘label’ => ‘Mot de passe’,
        ),
        ));
        $this->add(
        array(
        ‘name’ => ‘role’,
        ‘type’ => ‘Radio’,
        ‘attributes’ => array(
        ‘id’ => ‘type’
        ),
        ‘options’ => array(
        ‘label’ => ‘Role’,
        ‘value_options’ => array(
        ‘SuperAdmin’ => ‘SuperAdmin’,
        ‘Admin’ => ‘Admin’,
        ‘Utilisateur’ => ‘Utilisateur’
        )
        )
        )
        );
        $this->add(array(
        ‘name’ => ‘email’,
        ‘type’ => ‘Zend\Form\Element\Email’,
        ‘options’ => array(
        ‘label’ => ‘Email’,
        ),
        ));
        $this->add(array(
        ‘name’ => ‘submit’,
        ‘type’ => ‘Submit’,
        ‘attributes’ => array(
        ‘value’ => ‘Go’,
        ‘id’ => ‘submitbutton’,
        ),
        ));
        }
        }

        Model

        id = (isset($data['id'])) ? $data['id'] : null;
        $this->username = (isset($data['username'])) ? $data['username'] : null;
        $this->password = (isset($data['password'])) ? $data['password'] : null;
        $this->role = (isset($data['role'])) ? $data['role'] : null;
        $this->email = (isset($data['email'])) ? $data['email'] : null;
        }

        public function setInputFilter(InputFilterInterface $inputFilter)
        {
        throw new \Exception(“Not used”);
        }

        public function getInputFilter()
        {
        if (!$this->inputFilter) {
        $inputFilter = new InputFilter();
        $factory = new InputFactory();

        $inputFilter->add($factory->createInput(array(
        ‘name’ => ‘id’,
        ‘required’ => true,
        ‘filters’ => array(
        array(‘name’ => ‘Int’),
        ),
        )));

        $inputFilter->add($factory->createInput(array(
        ‘name’ => ‘username’,
        ‘required’ => true,
        ‘filters’ => array(
        array(‘name’ => ‘StripTags’),
        array(‘name’ => ‘StringTrim’),
        ),
        ‘validators’ => array(
        array(
        ‘name’ => ‘StringLength’,
        ‘options’ => array(
        ‘encoding’ => ‘UTF-8′,
        ‘min’ => 1,
        ‘max’ => 100,
        ),
        ),
        ),
        )));

        $inputFilter->add($factory->createInput(array(
        ‘name’ => ‘password’,
        ‘required’ => true,
        ‘filters’ => array(
        array(‘name’ => ‘StripTags’),
        array(‘name’ => ‘StringTrim’),
        ),
        ‘validators’ => array(
        array(
        ‘name’ => ‘StringLength’,
        ‘options’ => array(
        ‘encoding’ => ‘UTF-8′,
        ‘min’ => 1,
        ‘max’ => 100,
        ),
        ),
        ),
        )));

        $inputFilter->add($factory->createInput(array(
        ‘name’ => ‘role’,
        ‘required’ => true,
        ‘filters’ => array(
        array(‘name’ => ‘StripTags’),
        array(‘name’ => ‘StringTrim’),
        ),
        ‘validators’ => array(
        array(
        ‘name’ => ‘StringLength’,
        ‘options’ => array(
        ‘encoding’ => ‘UTF-8′,
        ‘min’ => 1,
        ‘max’ => 100,
        ),
        ),
        ),
        )));
        $inputFilter->add($factory->createInput(array(
        ‘name’ => ‘email’,
        ‘required’ => true,
        ‘filters’ => array(
        array(‘name’ => ‘StripTags’),
        array(‘name’ => ‘StringTrim’),
        ),
        ‘validators’ => array(
        array(
        ‘name’ => ‘StringLength’,
        ‘options’ => array(
        ‘encoding’ => ‘UTF-8′,
        ‘min’ => 1,
        ‘max’ => 100,
        ),
        ),
        ),
        )));

        $this->inputFilter = $inputFilter;
        }

        return $this->inputFilter;
        }
        public function getArrayCopy()
        {
        return get_object_vars($this);
        }

        }

  7. Alibdoul said, on June 26, 2013 at 7:54 pm

    Thx for you’r , i use this part of code for authentification and i add other field for database like “role” …. , o want to ask you how can i make a secific root for any member of user , for example if the user’s Admin when he connect he will be redurected automaticly to root “Admin” and if the user’s “visitor” he will be redirected to root “visitor” ???

  8. mohamedziada said, on July 1, 2013 at 6:03 am

    When u use “$this->identity()” in View
    it come with an error

    No AuthenticationService instance provided

    any idea why
    thank you

    • samsonasik said, on July 2, 2013 at 12:40 am

      $this->identity() view helper find service naming ‘Zend\Authentication\AuthenticationService’, so you should change ‘AuthService’ service with that :

      //config/module.config.php ....
          'service_manager' => array(
              'factories' => array(
                  'AuthStorage' => 'SanAuthWithDbSaveHandler\Factory\Storage\AuthStorageFactory',
                  'Zend\Authentication\AuthenticationService' => 'SanAuthWithDbSaveHandler\Factory\Storage\AuthenticationServiceFactory',
              ),
          ),
      

      and change the AuthControllerServiceFactory :

      //filename : module/SanAuthWithDbSaveHandler/src/SanAuthWithDbSaveHandler/Factory/Controller/AuthControllerServiceFactory.php
      namespace SanAuthWithDbSaveHandler\Factory\Controller;
      
      use Zend\ServiceManager\FactoryInterface;
      use Zend\ServiceManager\ServiceLocatorInterface;
      use SanAuthWithDbSaveHandler\Controller\AuthController;
      
      class AuthControllerServiceFactory implements FactoryInterface
      {
          public function createService(ServiceLocatorInterface $serviceLocator)
          {
              $authService = $serviceLocator->getServiceLocator()->get('Zend\Authentication\AuthenticationService');
              $controller = new AuthController($authService);
              
              return $controller;
          }
      }
      
      • mohamedziada said, on July 2, 2013 at 7:16 am

        Thank you for answer
        I found fast way, Just we need to

        ‘service_manager’ => array(
        ‘aliases’ => array(
        ‘Zend\Authentication\AuthenticationService’ => ‘AuthService’,
        ),
        ),

        It work fine with me

        My new Q now is:
        How can i make this Auth work fine with Subdomain

        I added in Config
        ‘session’ => array(
        ‘name’ => ‘my_namespace’,
        ‘use_cookies’ => true,
        ‘cookie_domain’ => ‘.YourZF2app ‘,
        ‘remember_me_seconds’ => 2419200,
        ‘cookie_httponly’ => true,
        ‘cookie_path’ => ‘/’,
        ‘cookie_lifetime’ => 2449,
        ‘gc_maxlifetime’ => 2449,
        ),

        and in AuthStorage Class
        $config = $this->getServiceLocator()->get(‘Config’);
        $sessionConfig->setOptions($config['session']);

        try
        $saveHandler->open($sessionConfig->getOption(‘save_path’), $this->namespace);
        and Also
        $saveHandler->open($sessionConfig->getOption(‘cookie_path’), $this->namespace);

        Not work with subdomain

        Any idea how can i do that

        Thank you man for this code

  9. mohamedziada said, on July 3, 2013 at 4:05 am

    Hi again
    My Fast trick is

    ini_set(‘session.cookie_path’, ‘/’);
    ini_set(‘session.cookie_domain’, ‘.zf2tutor.dev’);

    but it doesn’t update modified column in database .

    • samsonasik said, on July 4, 2013 at 2:37 am

      you can do something like this at AuthStorage::__construct

              $sessionconfig = new \Zend\Session\Config\SessionConfig;
              $sessionconfig->setStorageOption('cookie_domain', '.zf2tutor.dev');
              
              $this->session->getManager()->setConfig($this->sessionconfig);
      
  10. yannickk said, on July 10, 2013 at 7:02 pm

    Hi there,
    I get the following error as soon as I insert ‘Auth’ into the application.config.php

    Fatal error: Uncaught exception ‘Zend\ModuleManager\Exception\RuntimeException’ with message ‘Module (Auth) could not be initialized.’ in C:\DefaultWorkspace\skeletonAuth\vendor\ZF2\library\Zend\ModuleManager\ModuleManager.php:175 Stack trace: #0 C:\DefaultWorkspace\skeletonAuth\vendor\ZF2\library\Zend\ModuleManager\ModuleManager.php(149): Zend\ModuleManager\ModuleManager->loadModuleByName(Object(Zend\ModuleManager\ModuleEvent)) #1 C:\DefaultWorkspace\skeletonAuth\vendor\ZF2\library\Zend\ModuleManager\ModuleManager.php(90): Zend\ModuleManager\ModuleManager->loadModule(‘Auth’) #2 [internal function]: Zend\ModuleManager\ModuleManager->onLoadModules(Object(Zend\ModuleManager\ModuleEvent)) #3 C:\DefaultWorkspace\skeletonAuth\vendor\ZF2\library\Zend\EventManager\EventManager.php(468): call_user_func(Array, Object(Zend\ModuleManager\ModuleEvent)) #4 C: in C:\DefaultWorkspace\skeletonAuth\vendor\ZF2\library\Zend\ModuleManager\ModuleManager.php on line 175

    Best Regards

  11. Yogesh said, on July 17, 2013 at 10:21 pm

    Hi there,

    Sorry to bother you for an following issue.

    Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Zend\Db\Adapter\Adapter

    Could please help me to sort out this error?

    I have uploaded this screen capture to the URL https://docs.google.com/file/d/0B2owybOlptnxVGR6NXQwQXA5dE0/edit with public access but not sure if you can view it or not

    Composer.json snapshot

    “require”: {
    “php”: “>=5.3.3″,
    “zendframework/zendframework”: “=2.2.1″,
    “doctrine/doctrine-orm-module”: “0.7.*”,
    “zendframework/zend-developer-tools”: “dev-master”,
    “zendframework/zftool”: “dev-master”,
    “zf-commons/zfc-user-doctrine-orm”: “dev-master”
    }

  12. yogesh said, on July 19, 2013 at 8:12 pm

    I have one more query… how can I access session across all the controller?

    I added following code in Success Controller but it gives fatal error saying that 1st argument (of following connstructor ) must be an instance of AuthenticationService

    class SuccessController extends AbstractActionController
    {
    protected $authService;

    //we will inject authService via factory
    public function __construct(AuthenticationService $authService)
    {
    $this->authService = $authService;
    }
    ……

    I also tried following module.config.php but still error is there

    ‘controllers’ => array(
    ‘factories’ => array(
    ‘SanAuthWithDbSaveHandler\Controller\Auth’ => ‘SanAuthWithDbSaveHandler\Factory\Controller\AuthControllerServiceFactory’,
    ‘SanAuthWithDbSaveHandler\Controller\Success’ => ‘SanAuthWithDbSaveHandler\Factory\Controller\AuthControllerServiceFactory’
    ),

    It might be a very silly question but I am working on Zend framework for first time so please bear with me

    I also want to use this session in other controller like album controller and redirect user to login page if not logged in.

    Thanks for the help.

  13. Oubaydi said, on July 27, 2013 at 5:10 pm

    Great Thanks for you’r worck , i’m using you’rModule and all works fine . Just i m confuse how can i test if the user is logged in to use my application and if he’s not he will redirect automaticly to Auth interface . Now on my Application any one write the url on the browser can acced to the specific page even he’s not logged in , I used ZfcUser for other application and for this need i used this function

    if ($this->zfcUserAuthentication()->hasIdentity()) {
    //……//
    }
    else
    return $this->redirect()->toRoute(‘zfcuser’);

    is there any similar condition on you’r Module .

    great thx for this blog .

  14. Lucas CORBEAUX (@lucascorbeaux) said, on July 29, 2013 at 5:26 pm

    Hi there,

    Thanks for this very interesting article.

    As I mentioned on Twitter, I disagree a bit on the controller’s part of the code for the following reasons :
    * Controller authentication’s code is tight coupled to the underlying auth adapter.
    * Code intents is a bit complex and not that clear (more on this after).

    After reading the code, I spotted 5 use of the Auth service in the controller :
    * Checking if there is an identity.
    * Attempt to login.
    * Retrieve result row if success.
    * Store identity data.
    * Logout.

    To achieve this 5 goals, you used 16 method calls to the service and his dependencies : it’s a bit difficult to read, understand and maintain.

    I know it’s a bit out of the article’s scope, but I suggest implementing a really simple Facade pattern to clarify code intents and make the authentication easier to use. This Facade rely on a really simple interface of 4 methods :
    * login, that attempt to authenticate, and return the result row if succeed.
    * logout, that clear the login data.
    * hasIdentity, to check if a user is logged in or not.
    * storeIdentity, to save the data for later use.

    That way, low level authentication things can be refactored without hurting the application.

    A bit of code is better than a huge comment, so I forked your repo and commit my refactoring attempt : https://github.com/lucascorbeaux/SanAuthWithDbSaveHandler/commit/16229327d3ade4f80ff569c7ea3e1a40d118e79f

    It’s far from perfect as I don’t master ZF2 structure, the naming is not great too and there is no comments, but it gives the big picture.

    My two cents :)

    • samsonasik said, on July 29, 2013 at 6:53 pm

      Thank you very much. Btw, we should make the $result freely to decide where it to be placed, because we maybe need a $result->’getCode()’ to show to be catched, and redirect/fill message to the user. Feel free to make a PR, then I will review, and more than happy to merge it ;)

      • Lucas CORBEAUX (@lucascorbeaux) said, on July 29, 2013 at 7:05 pm

        Thank you for your reply, as suggested the PR has been sent.

        You’re right for the result, I’m not fan of this login method that authenticate and return the result row, as it’s not really extensible : I apply here a YAGNI approach to stick only to the need of the tutorial. Sure in a real world application this wouldn’t work for a long time.

      • samsonasik said, on July 30, 2013 at 11:39 am

        Ok, merged, thanks!

  15. Jeffery said, on July 30, 2013 at 7:47 pm

    what am I missing, does this example actually store the session data in the database as well? Or is this purely an authentication mechanism? I have tested the code and my session data is not in the session table.

    • samsonasik said, on August 1, 2013 at 6:38 pm

      just work at my side, please update your zf2 version to use latest master ;)

      • Jeffery said, on August 1, 2013 at 7:21 pm

        strange, I am using latest master (2 days old)

      • samsonasik said, on August 2, 2013 at 3:24 am

        Check your web server error log

  16. popson said, on July 31, 2013 at 1:34 am

    Hello,
    Thanks for your blog. Please write me how can I connect acl to this ?

  17. Sanjay.S.Nair said, on October 6, 2013 at 1:09 pm

    Really great tutorial. I have tried this. Working fine, session got saved to the db. But it is overwritten in a single row, even though I try different credentials.

  18. khalil said, on October 28, 2013 at 11:36 pm

    Hello,

    Thanks for your blog. Please i have probleme __construct() in my controller :

    Catchable fatal error: Argument 1 passed to Auth\Controller\AuthController::__construct() must be an instance of Zend\Authentication\AuthenticationService, none given, called in C:\wamp\www\Zf2\vendor\zendframework\zendframework\library\Zend\ServiceManager\AbstractPluginManager.php on line 170 and defined in C:\wamp\www\Zf2\module\Auth\src\Auth\Controller\AuthController.php on line 19

    • samsonasik said, on October 29, 2013 at 7:25 am

      you’re not follow the step by step :p. your controller is not created via factory :P

    • Ben said, on May 28, 2014 at 4:41 pm

      I got this error when I had my controller specified in both the ‘factories’ and ‘invokables’ sub sections of the ‘controllers’ section of the ‘module.config.php’ file. It should either be in one or the other. I think the controller was being invoked directly rather than via the factory so the constructor argument it expected wasn’t there.

  19. andrew said, on November 8, 2013 at 1:29 am

    Hello,

    How would you use the session save to the database in another module?

  20. Pradeep said, on November 19, 2013 at 1:21 am

    Many thanks for you blogs.. It has helped me at various points during development.

    I need to discuss about an issue I found. The functionality registering the save handler with the session manager is to automatically save the contents into the DB, if I am not mistaken. But I have been using a very similar AuthStorage implementation (except the explicit calls in write() and clear()) in my code and it does not save it automatically. When I explicitly set the manager in the constructor (by passing the session manager object), and it saves it automatically to the DB (without your calls in write() and clear()). And I think that is how it should be. Once you set the save handler in the container’s manager (either session storage or otherwise), the manager should take care of writing to the storage (_SESSION if supported) and persisting it in the DB.

    • samsonasik said, on November 19, 2013 at 3:04 am

      yes, but when authenticate() function of Authentication service got valid, it automatically save the identity to a session, I prevent it to expose more data ( ip address, role_id, and more). If you have any suggestion, please provide me a suggestion code for it ( as gist, pastebin, etc). Thank you.

  21. Will de la Vega said, on November 26, 2013 at 12:09 am

    Thanks very much for the good work you do for the ZF2 community. I always read your blog and get great ideas and inspiration from your articles. I have a question for you, I have a few apps in a Zend Developer Cloud container, I want to provide separate authentication for each app within the container, but it seems like the auth cookie is the same regardless of the folder in which the apps are. I understand this to be totally normal, while I need to change this behavior. I think that modifying the cookie to act as a dictionary collection that would store both, the id of the authenticated user and the app name, so that by changing the hasIdentity() method, it would check for the combination of both pieces of data before determining the user is authenticated or not before returning true or false.

    Is this the right way to go? could you please suggest any pointers on this regard? It is ok to copy the source file for AuthenticationService, do the mods and mark the changes as overrides? Is there any better way to do this or achieve the same result? Thanks very much in advance.

    • samsonasik said, on November 26, 2013 at 11:38 pm

      you can modify the session save path :

         $saveHandler
                  ->open("/path/to/folder/you/want", $this->namespace);
      

      and of course, change the session namespace from “my_storage_namespace” ( I provide ) to other name ;)

  22. jokowandiro said, on December 5, 2013 at 5:29 pm

    Good post about zend session, find helpful post to know more about zend session.

  23. Isaac said, on December 19, 2013 at 1:01 am

    Hi, i want to do a select before and from this write the results in the session storage but when i do it write empty in my var sessions.
    The select its okay, its only when i try to get the vars in the action.
    ¿How can pass the select to another action and get the result of the select?

    Please Help Me :'(
    I <3 your posts.

  24. Edgar said, on January 11, 2014 at 6:47 pm

    Hi very nice work, but i’m facing a problem, when the session expires the aplication is not redirecting to the Authentication page, i’m getting 404 The requested controller could not be mapped to an existing controller class. because it’s appending the base path:

    Controller:
    Application\Controller\SanAuthWithDbSaveHandler\Controller\Auth(resolves to invalid controller class or alias: Application\Controller\SanAuthWithDbSaveHandler\Controller\Auth)

    Can you help ?

  25. Dan said, on January 20, 2014 at 11:00 pm

    This was such a great little tutorial. I kept trying to use separate Session and Auth storage handlers and utterly frustrated that my sessions weren’t persisting! This helped me understand the Zend 2 system much better. Thank you.

  26. Azhar Ahmad said, on April 9, 2014 at 6:13 pm

    Can you tell me how can i fetch more than one records from the database using zend db 2.3 ???

  27. Azhar Ahmad said, on April 9, 2014 at 6:48 pm

    I am using the below code for fetching the records from the database but it didn’t give me the records

    $sql = new Sql($adapter);
    $select = $sql->select();
    $select->from(‘nf_client’);
    $select->where(array(‘client_id’ => $_GET['client_id']));

    $selectString = $sql->getSqlStringForSqlObject($select);
    $results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE);

  28. Azhar Ahmad said, on April 11, 2014 at 11:20 am

    How ???

    • samsonasik said, on April 13, 2014 at 7:29 am

      do effort please ;)

      • Azhar Ahmad said, on April 14, 2014 at 11:22 am

        Give me the hints of looping the $results

      • samsonasik said, on April 15, 2014 at 1:46 am
        foreach($results as $row) 
        {
            echo $row->columname;
        }
        

        that’s only hint, honestly, you need to learn php basic before use framework :)

      • Azhar Ahmad said, on April 15, 2014 at 11:11 am

        you don’t worry i know this loop but how can i use loop when it doesn’t give me a single row.If this give me a single row then i use the loop.

  29. Azhar Ahmad said, on April 15, 2014 at 11:18 am

    we can use this loop when the result is in associative array!!! but there is no array of data it just count the number of row

  30. Azhar Ahmad said, on April 15, 2014 at 11:46 am

    Thanx a lot it gives the expected result. I was thinking that it gives a simple array but i was wrong it give the result as ArrayObject. thanx once again:)

  31. Azhar Ahmad said, on April 15, 2014 at 11:53 am

    If i want left join then what should i do???

    • samsonasik said, on April 15, 2014 at 5:16 pm

      please read the docs and do effort, I’m not a helpdesk

      • Azhar Ahmad said, on April 16, 2014 at 11:04 am

        ok!!! don’t angry:)

  32. Zend framework 2 info | urlcatalog said, on April 23, 2014 at 11:12 pm

    […] AuthenticationService and Session Db […]

  33. drakantas said, on April 25, 2014 at 7:40 am

    Hi there! First of all, amazing tutorial. It’s very useful, hope ZF2 docs would be like this.
    After authenticate successfully, i am redirected to the “success page” but if i visit the login page again, it happens:

    An exception was raised while creating “AuthService”; no instance returned

    Then after a quick refresh, this problem is “autosolved”, like nothing happen.

    • samsonasik said, on April 26, 2014 at 5:29 am

      check error stack trace, you should got the reason.

      • drakantas said, on April 26, 2014 at 9:13 am

        I don’t know what just happened, but i’m not getting this error anymore.
        Thanks by the answer! Hope it doesn’t happen again.

        Also, thanks by everything.

  34. Azhar Ahmad said, on April 28, 2014 at 2:08 pm

    I have the following code to insert data into the database using zend db 2.3

    $adapter = new Zend\Db\Adapter\Adapter(array(
    ‘driver’ => ‘Mysqli’,
    ‘database’ => ‘nf_accounts’,
    ‘username’ => ‘root’,
    ‘password’ => ”
    ));
    $gross_total=array_sum($_POST['price']);
    $sql = new Insert($adapter);
    $sql->into(‘nf_invoices’);
    $sql->columns(array(‘invoice_no’,’client_id’,’createdby’,’created_datetime’));
    $sql->values(array(‘invoice_no’=>$_POST['invoice_no'],
    ‘client_id’=>$_POST['client_list'],
    ‘createdby’=>$_SESSION['DEAM_Username'],
    ‘created_datetime’=>date(“Y-m-d H:i:s”)),’set’);

    but it is not insert data into the database without any error.

    • Azhar Ahmad said, on April 28, 2014 at 5:52 pm

      neither it insert data into the database nor gives any error

  35. Kenia said, on May 19, 2014 at 7:21 am

    Hi Abdul. First of all, thank you so much for your help.

    I used the “AuthenticationService and Session Db Save Handler” approach in my authentication module.
    This works fine. Users are redirected to the login whenever there is no session. However I have a conflict with the authorization module. They both happen in the route event. Authentication has a higher priority. If The authorization module is enabled the redirect do not work. However when I change the redirect mechanic to the one shown here (#2) the redirect works fine.

    I wish I can make this redirect works within the MVC. Don’t like the idea of setting and sending the response back manually.

    This is current (approach # 1)
    if(!$sm->get(‘AuthService’)->getStorage()->getSessionManager()->getSaveHandler()->read($sm->get(‘AuthService’)->getStorage()->getSessionId())) {
    // Redirect to Login
    $e->getRouteMatch()->setParam(‘controller’, ‘login’)->setParam(‘action’, ‘index’);
    }

    approach # 2
    if(!$sm->get(‘AuthService’)->getStorage()->getSessionManager()->getSaveHandler()->read($sm->get(‘AuthService’)->getStorage()->getSessionId())) {

    // Redirect to Login
    $url = $e->getRouter()->assemble(array(‘action’ => ‘index’), array(‘name’ => ‘login’));
    $response = $e->getResponse();
    $response->getHeaders()->addHeaderLine(‘Location’, $url);
    $response->setStatusCode(302);
    $response->sendHeaders();
    exit;
    }

    Any suggestion of what to check for to make approach # 1 work?.
    I am not sure if has something to do with the fact that the controller and action
    are changed in approach #1 (when do “set”) but not the route name. I have that route name in the white list..which means “do not even bother to check authentication or authorization” but I haven’t be able to override the route name.

    Thanks very much in advance.

    • samsonasik said, on May 21, 2014 at 3:59 am

      hm.., have you apply it in the ‘dispatch’ event with higher priority ?

  36. budgeteldurham.com said, on July 5, 2014 at 4:05 pm

    I’m gone to inform my little brother, that he should also pay a visit this blog on regular basis to obtain updated from most
    up-to-date information.

  37. […] AuthenticationService and Session Db […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 243 other followers

%d bloggers like this: