i was learning OAuth2 in order to login an account with Google+ , Facebook, etc. on a web site.
I have followed a tutorial about it, and i have an issue when i clicked on my button "Connect with Google+" :
In the tutorial, he created a User.php but i had already one with FOS User.
Here is my code :
User.php :
<?php
// src/AppBundle/Entity/User.php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity
* @ORM\Table(name="fos_user")
*/
class User extends BaseUser implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string")
*/
protected $nom;
/**
* @ORM\Column(type="string")
*/
protected $prenom;
/**
* @ORM\Column(type="string")
*/
protected $nationalite;
/**
* @ORM\Column(type="datetime")
*/
protected $birthday;
/**
* @ORM\Column(type="string")
*/
protected $sexe;
/**
* @ORM\OneToOne(targetEntity="App\Entity\AnnonceColocation", mappedBy="User", cascade={"persist", "remove"})
*/
private $annonceColocation;
/**
* @ORM\OneToOne(targetEntity="App\Entity\AnnonceColocataire", mappedBy="User", cascade={"persist", "remove"})
*/
private $annonceColocataire;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Message", mappedBy="expediteur", orphanRemoval=true)
*/
private $sendMessage;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Message", mappedBy="destinataire", orphanRemoval=true)
*/
private $receivedMessage;
public function __construct()
{
parent::__construct();
$this->sendMessage = new ArrayCollection();
$this->receivedMessage = new ArrayCollection();
// your own logic
}
/**
* @return mixed
*/
public function getNom()
{
return $this->nom;
}
/**
* @param mixed $nom
*/
public function setNom($nom)
{
$this->nom = $nom;
}
/**
* @return mixed
*/
public function getPrenom()
{
return $this->prenom;
}
/**
* @param mixed $prenom
*/
public function setPrenom($prenom)
{
$this->prenom = $prenom;
}
public function getNationalite()
{
return $this->nationalite;
}
/**
* @param mixed $nationalite
*/
public function setNationalite($nationalite)
{
$this->nationalite = $nationalite;
}
public function getBirthday()
{
return $this->birthday;
}
/**
* @param mixed $birthday
*/
public function setBirthday($birthday)
{
$this->birthday = $birthday;
}
public function getSexe()
{
return $this->sexe;
}
/**
* @param mixed $sexe
*/
public function setSexe($sexe)
{
$this->sexe = $sexe;
}
public function getPicture()
{
return $this->picture;
}
/**
* @param mixed $picture
*/
public function setPicture($picture)
{
$this->picture = $picture;
}
public function getAnnonceColocation(): ?AnnonceColocation
{
return $this->annonceColocation;
}
public function setAnnonceColocation(AnnonceColocation $annonceColocation): self
{
$this->annonceColocation = $annonceColocation;
// set (or unset) the owning side of the relation if necessary
$newUser = $annonceColocation === null ? null : $this;
if ($newUser !== $annonceColocation->getUser()) {
$annonceColocation->setUser($newUser);
}
return $this;
}
public function getAnnonceColocataire(): ?AnnonceColocataire
{
return $this->annonceColocataire;
}
public function setAnnonceColocataire(?AnnonceColocataire $annonceColocataire): self
{
$this->annonceColocataire = $annonceColocataire;
// set (or unset) the owning side of the relation if necessary
$newUser = $annonceColocataire === null ? null : $this;
if ($newUser !== $annonceColocataire->getUser()) {
$annonceColocataire->setUser($newUser);
}
return $this;
}
/**
* @return Collection|Message[]
*/
public function getSendMessage(): Collection
{
return $this->sendMessage;
}
public function addSendMessage(Message $sendMessage): self
{
if (!$this->sendMessage->contains($sendMessage)) {
$this->sendMessage[] = $sendMessage;
$sendMessage->setExpediteur($this);
}
return $this;
}
public function removeSendMessage(Message $sendMessage): self
{
if ($this->sendMessage->contains($sendMessage)) {
$this->sendMessage->removeElement($sendMessage);
// set the owning side to null (unless already changed)
if ($sendMessage->getExpediteur() === $this) {
$sendMessage->setExpediteur(null);
}
}
return $this;
}
/**
* @return Collection|Message[]
*/
public function getReceivedMessage(): Collection
{
return $this->receivedMessage;
}
public function addReceivedMessage(Message $receivedMessage): self
{
if (!$this->receivedMessage->contains($receivedMessage)) {
$this->receivedMessage[] = $receivedMessage;
$receivedMessage->setDestinataire($this);
}
return $this;
}
public function removeReceivedMessage(Message $receivedMessage): self
{
if ($this->receivedMessage->contains($receivedMessage)) {
$this->receivedMessage->removeElement($receivedMessage);
// set the owning side to null (unless already changed)
if ($receivedMessage->getDestinataire() === $this) {
$receivedMessage->setDestinataire(null);
}
}
return $this;
}
public function getRoles()
{
return array('ROLE_USER');
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* @return string The password
*/
public function getPassword()
{
return null;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* @return string|null The salt
*/
public function getSalt()
{
return null;
}
/**
* Returns the username used to authenticate the user.
*
* @return string The username
*/
public function getUsername()
{
return $this->email;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
return null;
}
}
UserProvider.php :
<?php
/**
* Created by IntelliJ IDEA.
* User: mert
* Date: 12/18/17
* Time: 12:58 PM
*/
namespace App\Security;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class UserProvider implements UserProviderInterface
{
private $entityManager;
/**
* UserProvider constructor.
* @param EntityManagerInterface $entityManager
* @internal param Client $httpClient
* @internal param UserOptionService $userOptionService
* @internal param ProjectService $projectService
* @internal param SessionService $sessionService
* @internal param Session $session
* @internal param UserOptionService $userOptionsService
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Loads the user for the given username.
*
* This method must throw UsernameNotFoundException if the user is not
* found.
*
* @param string $username The username
*
* @return UserInterface
*
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function loadUserByUsername($username)
{
return $this->entityManager->createQueryBuilder('u')
->where('u.email = :email')
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
/**
* Refreshes the user.
*
* It is up to the implementation to decide if the user data should be
* totally reloaded (e.g. from the database), or if the UserInterface
* object can just be merged into some internal array of users / identity
* map.
*
* @param UserInterface $user
* @return UserInterface
*
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $user;
}
/**
* Whether this provider supports the given user class.
*
* @param string $class
*
* @return bool
*/
public function supportsClass($class)
{
return $class === 'App\Security\User';
}
}
GoogleController.php :
<?php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class GoogleController extends AbstractController
{
/**
* Link to this controller to start the "connect" process
*
* @Route("/connect/google", name="connect_google")
* @param ClientRegistry $clientRegistry
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function connectAction(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('google')
->redirect();
}
/**
* Facebook redirects to back here afterwards
*
* @Route("/connect/google/check", name="connect_google_check")
* @param Request $request
* @return JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function connectCheckAction(Request $request)
{
if (!$this->getUser()) {
return new JsonResponse(array('status' => false, 'message' => "User not found!"));
} else {
return $this->redirectToRoute('default');
}
}
}
and my GoogleAuthenticator.php :
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use League\OAuth2\Client\Provider\GoogleUser;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* Created by IntelliJ IDEA.
* User: mert
* Date: 12/18/17
* Time: 12:00 PM
*/
class GoogleAuthenticator extends SocialAuthenticator
{
private $clientRegistry;
private $em;
private $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $em, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->em = $em;
$this->router = $router;
}
public function supports(Request $request)
{
return $request->getPathInfo() == '/connect/google/check' && $request->isMethod('GET');
}
public function getCredentials(Request $request)
{
return $this->fetchAccessToken($this->getGoogleClient());
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
/** @var GoogleUser $googleUser */
$googleUser = $this->getGoogleClient()
->fetchUserFromToken($credentials);
$email = $googleUser->getEmail();
$user = $this->em->getRepository('App:User')
->findOneBy(['email' => $email]);
if (!$user) {
$user = new User();
$user->setEmail($googleUser->getEmail());
$user->setFullname($googleUser->getName());
$user->setCreatedAt(new \DateTime(date('Y-m-d H:i:s')));
$this->em->persist($user);
$this->em->flush();
}
return $user;
}
/**
* @return \KnpU\OAuth2ClientBundle\Client\OAuth2Client
*/
private function getGoogleClient()
{
return $this->clientRegistry
->getClient('google');
}
/**
* Returns a response that directs the user to authenticate.
*
* This is called when an anonymous request accesses a resource that
* requires authentication. The job of this method is to return some
* response that "helps" the user start into the authentication process.
*
* Examples:
* A) For a form login, you might redirect to the login page
* return new RedirectResponse('/login');
* B) For an API token authentication system, you return a 401 response
* return new Response('Auth header required', 401);
*
* @param Request $request The request that resulted in an AuthenticationException
* @param \Symfony\Component\Security\Core\Exception\AuthenticationException $authException The exception that started the authentication process
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function start(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $authException = null)
{
return new RedirectResponse('/login');
}
/**
* Called when authentication executed, but failed (e.g. wrong username password).
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the login page or a 403 response.
*
* If you return null, the request will continue, but the user will
* not be authenticated. This is probably not what you want to do.
*
* @param Request $request
* @param \Symfony\Component\Security\Core\Exception\AuthenticationException $exception
*
* @return \Symfony\Component\HttpFoundation\Response|null
*/
public function onAuthenticationFailure(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $exception)
{
// TODO: Implement onAuthenticationFailure() method.
}
/**
* Called when authentication executed and was successful!
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the last page they visited.
*
*
If you return null, the current request will continue, and the user * will be authenticated. This makes sense, for example, with an API. * * @param Request $request * @param \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token * @param string $providerKey The provider (i.e. firewall) key * * @return void */ public function onAuthenticationSuccess(Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token, $providerKey) { // TODO: Implement onAuthenticationSuccess() method. } }
Hope some of you could help on this. Thanks a lot.