andykibz
andykibz

Reputation: 31

HWIOAuthBundle and Custom UserBundle can't seem to work together

I can't seem to get Hwioauthbundle to work with my custom userbundle in symfony3. I would like to use googleoauth for my application,where a user can signin with google and also use a a registration form. Either method will store the user in the database.The registration form by itself works well but on introducing hwioauthbundle,things wouldn't work out for me. Some of the issues I've encountered include:

  1. On opening my login page, I get a blank page with a link with the text "google",this is contrary to what I expect which is a login form. However if I change the login page to a route other than anything close to "login" and "signin",the form is displayed.
  2. Surprisingly the link does what I want it to i.e it redirects to google's permissions page but after allowing it to proceed I get "Error while sending HTTP request" message.
  3. when opening the url "/login/check-google" I get a "No oauth code in the request." text followed by the same google link in the previous cases.

I've done numerous searches but I can't seem to figure out the issue. Any help with regard to where I am wrong or even alternative will be highly appreciated

providers:
        intersect_provider:
            entity:
                class: UserBundle:User
                property: username
firewalls:

    # disables authentication for assets and the profiler, adapt it according to your needs
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    main:
        anonymous: ~
        guard:
            authenticators:
                - intersect.authenticator.form_login
            entry_point: intersect.authenticator.form_login
        form_login:
            provider: intersect_provider
            login_path: /login
            check_path: /login
        logout:
            path:   /signout
            target: /
        oauth:
            resource_owners:
                google:             "/login/check-google"
            login_path: /
            use_forward:       false
            failure_path:      /login

            oauth_user_provider:
                service: intersect.authenticator.oauth

Security.yml file

hwi_oauth:
firewall_names: [ "main" ]
resource_owners:
    google:
        type: google
        client_id:  <clientid>
        client_secret:  <secret>
        scope: "email profile"
        options:
            access_type:  offline
            csrf: true

config.yml file

 <?php

namespace Intersect\UserBundle\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder;
use Symfony\Component\Security\Core\Security;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    private $em;
    private $passwordEncoder;
    private $router;

    public function __construct(EntityManager $em, RouterInterface $router, UserPasswordEncoder $passwordEncoder )
    {
        $this->passwordEncoder = $passwordEncoder;
        $this->em = $em;
        $this->router = $router;
    }

    public function getCredentials(Request $request)
    {
        $isLoginSubmit = $request->getPathInfo() == '/login' && $request->isMethod('POST');
        if(!$isLoginSubmit){
            return;
        }
        $username = $request->request->get('_username');
        $request->getSession()->set(Security::LAST_USERNAME, $username);
        $password = $request->request->get('_password');

        return [
            'username'=>$username,
            'password'=>$password
        ];

    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $username=$credentials['username'];
        return $this->em->getRepository('UserBundle:User')->findByUsernameOrEmail($username);

    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        $password=$credentials['password'];
        if ($this->passwordEncoder->isPasswordValid($user, $password)) {
            return true;
        }
        return false;
    }

    protected function getLoginUrl()
    {
        return $this->router->generate('intersect_login');
    }

    protected function getDefaultSuccessRedirectUrl()
    {
        return $this->router->generate('homepage');
    }

}

My form authenticator: LoginFormAuthenticator.php

parameters:
#    parameter_name: value

services:
#    service_name:
#        class: AppBundle\Directory\ClassName
#        arguments: ["@another_service_name", "plain_value", "%parameter_name%"]
    intersect.authenticator.form_login:
        class: Intersect\UserBundle\Security\LoginFormAuthenticator
        autowire: true
        # autowire: true

    hwi_oauth.user.provider.entity:
        class: HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider

    intersect.authenticator.oauth:
        class: Intersect\UserBundle\Security\OAuthProvider
        arguments:
            - '@session'
            - '@doctrine'
            - '@service_container'

services.yml file

hwi_oauth_login:
resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
prefix:   /login

hwi_oauth_redirect:
    resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
    prefix:   /login

google_login:
    path: /login/check-google

user:
    resource: "@UserBundle/Controller/"
    type:     annotation
    prefix:   /

app:
    resource: "@AppBundle/Controller/"
    type:     annotation

Routing.yml file

<?php

namespace Intersect\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Intersect\UserBundle\Entity\User;

class SecurityController extends Controller
{

  /**
   * @Route("/login",name="intersect_login")
   */
   public function loginAction(){
     $helper = $this->get('security.authentication_utils');

     return $this->render('Intersect/login.html.twig',[
           'last_username' => $helper->getLastUsername(),
           'error' => $helper->getLastAuthenticationError(),
        ]
     );

   }

  /**
   * @Route("/signup",name="intersect_signup")
   */
  public function signupAction(Request $request)
  {

      $user = new User;
      $regform = $this->createForm('Intersect\UserBundle\Form\SignUpType', $user);

      $regform->handleRequest($request);
      if($regform->isSubmitted() && $regform->isValid()){
          $encoder = $this->container->get('security.password_encoder');
          $password = $encoder->encodePassword($user, $user->getPlainPassword());
          $user->setPassword($password);

          $em = $this->getDoctrine()->getManager();
          $em->persist($user);
          $em->flush();

          return $this->redirectToRoute('homepage');
      }

      return $this->render('Intersect/signup.html.twig',[
        'regform'=>$regform->createView()
      ]);
    }
}

SecurityController file

<?php
namespace Intersect\UserBundle\Security;

use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Intersect\UserBundle\Entity\User;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Security;

class OAuthProvider extends OAuthUserProvider
{
    protected $session, $doctrine, $admins;

    public function __construct($session, $doctrine, $service_container)
    {
        $this->session = $session;
        $this->doctrine = $doctrine;
        $this->container = $service_container;
    }

    public function loadUserByUsername($username)
    {

        $qb = $this->doctrine->getManager()->createQueryBuilder();
        $qb->select('u')
            ->from('UserBundle:User', 'u')
            ->where('u.googleId = :gid')
            ->setParameter('gid', $username)
            ->setMaxResults(1);
        $result = $qb->getQuery()->getResult();

        if (count($result)) {
            return $result[0];
        } else {
            return new User();
        }
    }

    public function saveOauthUserResponse(UserResponseInterface $response)
    {
      //data from google
      $google_Id=$response->$response->getUserName();
      $email=$response->getEmail();
      // $username = $response->getRealName();
      // $avatar = $response->getProfilePicture();

      //save googele data
      $this->session->set('email', $email);
      $this->session->set('username', $nickname);
      // $this->session->set('realname', $realname);

      $qb = $this->doctrine->getManager()->createQueryBuilder();
        $qb->select('u')
            ->from('UserBundle:User', 'u')
            ->where('u.oauth_Id = :gid')
            ->setParameter('gid', $google_Id)
            ->setMaxResults(1);
        $result = $qb->getQuery()->getResult();

        //add to database if doesn't exists
        if (!count($result)) {
            $user = new User();
            $user->setUsername($username);
            $user->setEmail($email);
            $user->setoauth_Id($oauth_Id);
            //$user->setRoles('ROLE_USER');

            //Set some wild random pass since its irrelevant, this is Google login
              // $factory = $this->container->get('security.encoder_factory');
              // $encoder = $factory->getEncoder($user);
              // $password = $encoder->encodePassword(md5(uniqid()), $user->getSalt());
              // $user->setPassword($password);

            $em = $this->doctrine->getManager();
            $em->persist($user);
            $em->flush();
        } else {
            $user = $result[0]; /* return User */
        }
        //set id
        $this->session->set('id', $user->getId());

        return $doctrine->findByUsernameOrEmail($response->getUsername());

    }
}

OauthProvider. I am aware this code is not perfect, but I feel like it is not the reason why I am getting the issues mentioned earlier.

Upvotes: 2

Views: 931

Answers (1)

andykibz
andykibz

Reputation: 31

I think I've solved the issues I mentioned earlier however I'm still not done with the whole thing. How I solved

I changed the hwi_oauth_login prefix to

/connect

from

/login

In Routing.yml

hwi_oauth_login:
    resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
    prefix:   /connect

Then added a forward slash to my login route in SecurityController.php

/**
   * @Route("/login/",name="intersect_login")
   */
   public function loginAction(){
     $helper = $this->get('security.authentication_utils');

     return $this->render('Intersect/signin.html.twig',[
           'last_username' => $helper->getLastUsername(),
           'error' => $helper->getLastAuthenticationError(),
        ]
     );

   }

The url used to start google authenication is

/connect/<\resource_owner>

where <\resource_owner> is placeholder for "google"

Now I'm getting a

You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine.

error which I hope I can be able to get a solution from other threads.

Upvotes: 1

Related Questions