ZF2 : Zend_Auth with Doctrine how to to bypass setCredentialValue check, ie when Password check is not required

511 Views Asked by At

I have Implemented a login module with Zend_Auth & Doctrine , and its working fine as expected, now that I have implemented Social login too in the module, in this case I need to bypass Password check , as Password are not required in such cases once the user is validated by 3rd party providers(eg Facebook etc).

So I have modified by function in case of social login attempt ,

In controller

$authService = $this->getServiceLocator()->get('Zend\Authentication\AuthenticationService');
$adapter = $authService->getAdapter();
$adapter->setIdentityValue($userProfile->email);
$adapter->setCredentialValue('NA'); //  <----- I have used "NA" to notify its an social login

And in module.config.php

'doctrine' => array(
        'authentication' => array(
            'orm_default' => array(
                'object_manager' => 'Doctrine\ORM\EntityManager',
                'identity_class' => 'Account\Entity\Account',
                'identity_property' => 'username',
                'credential_property' => 'password',
                'credential_callable' => function(Entity\Account $user, $passwordGiven) {
                    if($passwordGiven == 'NA')
                        return true;
                    else
                        return md5($passwordGiven) == $user->getHpassword();
                },
            )
        ),

its working fine , but I am not sure is it the correct way to do it ? Any suggestion or more feasible way to do it.

1

There are 1 best solutions below

8
Wilt On

The doctrine authentication classes are registered here in the module.config.php from DoctrineModule:

'doctrine' => array(
    'doctrine_factories' => array(
        'cache'                 => 'DoctrineModule\Service\CacheFactory',
        'eventmanager'          => 'DoctrineModule\Service\EventManagerFactory',
        'driver'                => 'DoctrineModule\Service\DriverFactory',
        'authenticationadapter' => 'DoctrineModule\Service\Authentication\AdapterFactory',
        'authenticationstorage' => 'DoctrineModule\Service\Authentication\StorageFactory',
        'authenticationservice' => 'DoctrineModule\Service\Authentication\AuthenticationServiceFactory',
    )
)

You can easily overwrite these configurations with your custom factories.

I am not sure what is the best way to manage all this since I have no clue what your authentication with your social service looks like. You can overwrite any of those factories and add your custom logic to inject your Identity from your social authentication service.

But maybe it would even be better to not use doctrine authentication at all but combine both authentication solutions into one custom authentication service class. That is up to you.

I will can give an example for setting the $identity from you social service in the doctrine authentication storage by overwriting the storage factory class. This will give you an idea how to work with these things.

In your application module.config.php overwrite the key for the storage factory with your custom factory class (make sure your module is loaded after doctrine module so the key is overwritten on config merging):

'doctrine' => array(
    'doctrine_factories' => array(
        'authenticationstorage' => 'Application\Service\Authentication\CustomStorageFactory',
    )
)

Now create this custom class in your application folder. Your CustomStorageFactory could for example look like this:

<?php

namespace Application\Service\Authentication;

use DoctrineModule\Authentication\Storage\ObjectRepository;
use Zend\ServiceManager\ServiceLocatorInterface;
use DoctrineModule\Service\Authentication\StorageFactory;

/**
 * Customized factory to inject identity from social authentication
 */
class CustomStorageFactory extends StorageFactory
{
    /**
     * @param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator
     * @return \Zend\Authentication\AuthenticationService
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        // Construct storage like normally by calling parent class
        $doctrineStorage = parent::__construct($serviceLocator);

        // Write the identity into the storage
        $socialService = $serviceLocator->get('SocialAuthenticationService');
        if($socialService->hasIdentity()){
            $identity = $socialService->getIdentity();
            $doctrineStorage->write($identity);
        }

        // return the storage
        return $doctrineStorage;
    }
}

Now you can do something like this:

$serviceLocator= $this->getServiceLocator();
$authService = $serviceLocator->get(`Zend\Authentication\AuthenticationService`);

// Check if we have identity, if not we authenticate
if(!$authService->hasIdentity()){
    $authService->authenticate();
)
$authService->getIdentity();