Zend Framework 2 : Using Custom Authentication condition with DoctrineModule
In DoctrineModule, there is a way to use authentication functionality that call specific entity and its properties with identity and credential. What if we need to use custom authentication conditional, like when user has is_active = 1 or is_enabled = 1. For example, we have a table structure like the following :
CREATE TABLE IF NOT EXISTS `User` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `is_active` smallint(6) NOT NULL DEFAULT '1', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
There is a is_active field that need to be checked when authentication process excecuted. What we need to do, is to create a custom Authentication adapter objectrepository that extends DoctrineModule\Authentication\Adapter\ObjectRepository :
namespace MyDoctrineAuth\Adapter; use DoctrineModule\Authentication\Adapter\ObjectRepository as BaseObjectRepository; use Zend\Authentication\Result as AuthenticationResult; class ObjectRepository extends BaseObjectRepository { /** * {@inheritDoc} */ public function authenticate() { $this->setup(); $options = $this->options; $identity = $options ->getObjectRepository() ->findOneBy(array( $options->getIdentityProperty() => $this->identity, // with assumption, our entity use $isActive property 'isActive' => 1, )); if (!$identity) { $this->authenticationResultInfo['code'] = AuthenticationResult::FAILURE_IDENTITY_NOT_FOUND; $this->authenticationResultInfo['messages'][] = 'A record with the supplied identity could not be found.'; return $this->createAuthenticationResult(); } $authResult = $this->validateIdentity($identity); return $authResult; } }
The custom ObjectRepository above override the authenticate() method with adding more condition into findOneBy method.
Now, we need to create a factory to instantiate its class :
namespace MyDoctrineAuth\Factory\Authentication; use DoctrineModule\Service\Authentication\AdapterFactory as BaseAdapterFactory; use MyDoctrineAuth\Adapter\ObjectRepository; use Zend\ServiceManager\ServiceLocatorInterface; class AdapterFactory extends BaseAdapterFactory { /** * {@inheritDoc} * * @return \MyDoctrineAuth\Adapter\ObjectRepository */ public function createService(ServiceLocatorInterface $serviceLocator) { /* @var $options \DoctrineModule\Options\Authentication */ $options = $this->getOptions($serviceLocator, 'authentication'); if (is_string($objectManager = $options->getObjectManager())) { $options->setObjectManager($serviceLocator->get($objectManager)); } return new ObjectRepository($options); } }
Great!, time to register the AdapterFactory into module.config.php :
return array( 'doctrine_factories' => array( 'authenticationadapter' => 'MyDoctrineAuth\Factory\Authentication\AdapterFactory', ), ),
You can grab sample of above code here : https://github.com/samsonasik/MyDoctrineAuth .
I think your blog is one of the best source of zf2, keep going!
thank you ;). I will 😉
I tried this example and it didn’t worked for me. Seems like the rewrited adapter is not registered in Authentication Service.
make sure you use DoctrineModule
Hi ! thanks for this great example.
I wonder how I can setup timeout authentication with your example ? Thanks
define your storage that have session timeout, and set via AuthenticationService
Thanks. the problem i still faced is inability to define error messages per result, and that’s why i created a module to do just that https://github.com/oromedialab/doctrine-module-auth-extension
I can’t seem to get this working… Where is it defined that the custom authenticationadapter should be used?
I noticed that my program still uses the default adapters from vendor folder instead of the custom one. I can even comment out ‘doctrine_factories’ => array( ‘authenticationadapter’ => ‘MyDoctrineAuth\Factory\Authentication\AdapterFactory’) and nothing changes. Any idea what I might be missing here?
I have gone throug the complete code on github and doublechecked that everything should be the same. Any help would be apprechiated
in your own module config, see the sample as I mentioned in the article: https://github.com/samsonasik/MyDoctrineAuth
Well, I have it checked that my module.config.php has the same settings as yours, but still the ‘DoctrineModule\Service\Authentication\AdapterFactory’ is being used when I run the program. Must be some other configuration that overrides my settings…
The only way I seem to get this config to work is if I comment out line 97 in ./vendor/doctrine/doctrine-module/config/module.config.php, where the default adapter is set (‘authenticationadapter’ => ‘DoctrineModule\Service\Authentication\AdapterFactory’,) but that obviously is unacceptable
check the sort order of module, that’s may be not overriden due merging process
THANK YOU!! 😀 your last tip about checking the module order fixed my problems! I needed to put the MyDoctrineAuth module to appear after DoctrineModule in application.config.php.
Once again a very simple fix to problem that has caused me hours of headaches. Your blog is awesome and so much more descriptive than zend’s own documentation!
Overriding authenticate() method is not a right way because you will never know if an identity is not found or it is not active. For the above use case, you should always use credential_callable in config and you even don’t need to write your own custom class for that.
Thank you for the suggestion, I didn’t try credential_callable, I may try if I have a chance
Absolutely correct that the smallest of tweaks in your callback is enough to check the is_active property at the same time you’re checking the password. But it does seem that you need to extend Zend\Authentication\Result and add another class constant like FAILURE_ACCOUNT_INACTIVE if you want your caller to learn more about why authentication failed. And that implies overriding the adapter after all.
Or am I mistaken?
hm.., I tried my way in a post and just works. If you have a step to reproduce with better way, I think you can write a blog post, so I may link your post as a better way 😉
Hello,
I’m currently trying to create a custom authentication in a project based on ZF and Doctrine 2.5.
I tried to set up the process you make here, but I get the following error: “Zend \ ServiceManager \ ServiceManager :: get Was Unable to fetch or create an instance for Zend \ Authentication \ AuthenticationAdapter”.
The relevant line seems to be in the controllers configuration file:
return
[
‘Controllers’ =>
[
‘Factories’ =>
[
‘User \ Controller \ Auth’ => function ($ controller) {
$ AuthController = new User \ Controller \ AuthController (
$ controller
-> GetServiceLocator ()
-> Get ( ‘Zend \ Authentication \ AuthenticationAdapter’));
return $ AuthController;
}
]
]
];
And more specifically -> get ( ‘Zend \ Authentication \ AuthenticationAdapter’));
Thank you for the help!
that error show you that the service named `Zend \ Authentication \ AuthenticationAdapter` not registered, please make sure called registered service.