phpsymfonyhwioauthbundle

Facebook Login - HWIOauthBundle - Update FacebookID if user found


I had implemented HWIOauthBundle - Facebook Login into my symfony app.

I have a problem if a user were already registered using FOSUserBundle, before Facebook Login.

If I have the user, and try to login with facebook, Facebook ID won't update in the database if the E-mail already exists.

Where should I put my code, that verifies if an E-mail exists, and if yes, to update the facebook_id column after the Facebook connection?


Solution

  • You can extend the HWIOauthBundle's FOSUBUserProvider class and implement required logic. Example (relevant for HWIOauthBundle 0.4.x):

    src/AppBundle/Security/Core/User/FOSUBUserProvider.php

    <?php
    
    namespace AppBundle\Security\Core\User;
    
    use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
    use HWI\Bundle\OAuthBundle\Security\Core\Exception\AccountNotLinkedException;
    use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
    use AppBundle\Entity\User;
    use Symfony\Component\Security\Core\User\UserInterface;
    use AppBundle\Event\UserEvent;
    use AppBundle\Doctrine\UserManager;
    
    class FOSUBUserProvider extends BaseClass
    {
        /**
         * {@inheritDoc}
         */
        public function connect(UserInterface $user, UserResponseInterface $response)
        {
            $id = $response->getUsername();
    
            $service = $response->getResourceOwner()->getName();
            $setter = 'set' . ucfirst($service);
            $setterId = $setter . 'Id';
            $setterToken = $setter . 'AccessToken';
    
            //disconnect previously connected user
            if (null !== $previousUser = $this->userManager->findUserBy([$this->getProperty($response) => $id])) {
                $previousUser->$setterId(null);
                $previousUser->$setterToken(null);
                $this->userManager->updateUser($previousUser);
            }
    
            //connect current user
            $user->$setterId($id);
            $user->$setterToken($response->getAccessToken());
    
            $this->userManager->updateUser($user);
        }
    
        /**
         * {@inheritdoc}
         */
        public function loadUserByOAuthUserResponse(UserResponseInterface $response)
        {
            $id = $response->getUsername();
            $email = $response->getEmail();
    
            if (empty($email)) {
                throw new AccountNotLinkedException("Email is empty");
            }
    
            /** @var User $user */
            $user = $this->userManager->findUserBy([$this->getProperty($response) => $id]);
            if (null === $user) {
                $user = $this->userManager->findUserByEmail($email);
    
                $service = $response->getResourceOwner()->getName();
                $setter = 'set' . ucfirst($service);
                $setterId = $setter . 'Id';
                $setterToken = $setter . 'AccessToken';
    
                if (null === $user) {
                    $user = $this->userManager->createUser();
                    $user->$setterId($id);
                    $user->$setterToken($response->getAccessToken());
    
                    $user->setUsername($id)
                        ->setFirstName($response->getFirstName())
                        ->setLastName($response->getLastName() ?: $response->getRealName())
                        ->setPassword('123456');
    
                    $user->setEmail($email);
                    $user->setEnabled(true);
                    $this->userManager->updateUser($user);
    
                    return $user;
                } else {
                    $user->$setterId($id);
                    $user->$setterToken($response->getAccessToken());
    
                    $this->userManager->updateUser($user);
    
                    return $user;
                }
            }
    
            //if user exists - go with the HWIOAuth way
            $user = parent::loadUserByOAuthUserResponse($response);
    
            $serviceName = $response->getResourceOwner()->getName();
            $setter = 'set' . ucfirst($serviceName) . 'AccessToken';
    
            //update access token
            $user->$setter($response->getAccessToken());
    
            return $user;
        }
    }
    

    services.yml

    services:
        app.my_user_provider:
            class: AppBundle\Security\Core\User\FOSUBUserProvider
            arguments: ['@fos_user.user_manager', { facebook: 'facebookId' }]
    

    config.yml

    hwi_oauth:
        connect:
            account_connector: app.my_user_provider
    

    security.yml

            oauth:
                resource_owners:
                    facebook: "/login/check-facebook"
                login_path: /login
                failure_path: /login
                oauth_user_provider:
                    service: app.my_user_provider