1

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.

Dimag Kharab
  • 4,439
  • 1
  • 24
  • 45

1 Answers1

1

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();
Wilt
  • 41,477
  • 12
  • 152
  • 203
  • Looks awesome , will implement and let you know ! thanks – Dimag Kharab Oct 29 '15 at 10:12
  • @n01ze You could also extend the existing Doctrine adapter class and inject the identity directly in there. Not sure what will work better... – Wilt Oct 29 '15 at 10:14
  • How I am gonna pass "$identityFromSocial" to the custom adaptor ? in the service manager call ?? Oh i am lil lost – Dimag Kharab Oct 29 '15 at 10:17
  • 1
    @n01ze In a factory. You will have to make a factory and inject it there. – Wilt Oct 29 '15 at 11:33
  • nope really, I got your point , you were saying to overwrite the doctrine authentication adapter, and pass the social login in constructor, but I am not getting any clue how to put this thing up in my code. I mean how to call , overwrite . PS: newbie to Zend ( m from python django field) – Dimag Kharab Oct 30 '15 at 05:11
  • 1
    @n01ze I updated my answer with a more useful example. I hope this will give you an idea of the possibilities and how to overwrite the existing factories. I won't claim this is the best solution, that totally depends on your application. – Wilt Oct 30 '15 at 08:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93784/discussion-between-n01ze-and-wilt). – Dimag Kharab Oct 30 '15 at 09:25
  • Hi I have implemented your class , it got overwritten to , but I am again back to the problem , write is not working as it need OBJECT created by authenticate only – Dimag Kharab Oct 30 '15 at 10:14