Dans cet article on va voir comment se connecter à un site via vos identifiants Google ou facebook, … (la liste complete ici : https://github.com/knpuniversity/oauth2-client-bundle#configuring-a-client) et on va partir du principe que l’on se connecte exclusivement avec cette méthode (et donc pas de mot de passe)
On va d’abord avoir d’une entité User, voici à quoi ressemble la mienne :
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
class User implements UserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private $email;
#[ORM\Column(type: 'json')]
private $roles = [];
#[ORM\Column(type: 'string', length: 255)]
private $googleId;
#[ORM\Column(type: 'text', nullable: true)]
private $avatar;
#[ORM\Column(type: 'string', length: 255)]
private $hostedDomain;
On va avoir besoin du « knpUOAuth2ClientBundle » :
composer require knpuniversity/oauth2-client-bundle
Ainsi que Provider que vous souhaitez installer (pour cette article ce sera google) :
composer require league/oauth2-google
Ensuite c’est beaucoup de configuration, d’abord dans le fichier config/packages/knpu_oauth2_client.yaml :
knpu_oauth2_client:
clients:
google:
type: google
client_id: '%env(resolve:GOOGLE_CLIENT_ID)%'
client_secret: '%env(resolve:GOOGLE_CLIENT_SECRET)%'
redirect_route: connect_google_check
redirect_params: {}
2 choses à renseigner dans votre .env : L’id google et le client secret que vous devez recuperer depuis votre console google. Et on va tout de suite créer le controller qui va gérer la connexion google :
<?php
# Controller/GoogleController
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class GoogleController extends AbstractController
{
#[Route('/connect/google', name: 'connect_google')]
public function connectAction(ClientRegistry $clientRegistry)
{
//Redirect to google
return $clientRegistry->getClient('google')->redirect([], []);
}
/**
* After going to Google, you're redirected back here
* because this is the "redirect_route" you configured
* in config/packages/knpu_oauth2_client.yaml
*/
#[Route('/connect/google/check', name: 'connect_google_check')]
public function connectCheckAction(Request $request)
{
// ** if you want to *authenticate* the user, then
// leave this method blank and create a Guard authenticator
}
}
A ce stade on arrive à se connecter à Google et recuperer les infos de l’utilisateurs, mais on est pas connecté à notre appli. Ce que l’on va faire maintenant en utilisant OAuth2Authenticator (vous pouvez utiliser le maker-bundle auth et « empty authenticator » puis modifier la classe héritée) :
<?php
# src/Security/GoogleAuthenticator.php
namespace App\Security;
use App\Entity\User;
use League\OAuth2\Client\Provider\GoogleUser;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
class GoogleAuthenticator extends OAuth2Authenticator
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $entityManager;
private RouterInterface $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->entityManager = $entityManager;
$this->router = $router;
}
public function supports(Request $request): ?bool
{
// continue ONLY if the current ROUTE matches the check ROUTE
return $request->attributes->get('_route') === 'connect_google_check';
}
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('google');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {
/** @var GoogleUser $googleUser */
$googleUser = $client->fetchUserFromToken($accessToken);
$email = $googleUser->getEmail();
// have they logged in with Google before? Easy!
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['googleId' => $googleUser->getId()]);
//User doesnt exist, we create it !
if (!$existingUser) {
$existingUser = new User();
$existingUser->setEmail($email);
$existingUser->setGoogleId($googleUser->getId());
$existingUser->setHostedDomain($googleUser->getHostedDomain());
$this->entityManager->persist($existingUser);
}
$existingUser->setAvatar($googleUser->getAvatar());
$this->entityManager->flush();
return $existingUser;
})
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// change "app_dashboard" to some route in your app
return new RedirectResponse(
$this->router->generate('app_dashboard')
);
// or, on success, let the request continue to be handled by the controller
//return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
// public function start(Request $request, AuthenticationException $authException = null): Response
// {
// /*
// * If you would like this class to control what happens when an anonymous user accesses a
// * protected page (e.g. redirect to /login), uncomment this method and make this class
// * implement Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface.
// *
// * For more details, see https://symfony.com/doc/current/security/experimental_authenticators.html#configuring-the-authentication-entry-point
// */
// }
}
Et voilà ! Vous pouvez maintenant aller sur /google/connect et vous serez connecté automatiquement à votre appli.
[…] Symfony 6: S’authentifier avec Google, Facebook, GitHub, … […]
[…] Symfony 6: S’authentifier avec Google, Facebook, GitHub, … […]
Hello, ça fonctionne pas chez moi, avec symfony 6; tu dois pas enregistrer le custom auth dans le security.yaml ? Si je le fais ça marche pas, et si je le fais pas il me dit que la fonction checkAction doit retourner une réponse 🙁
Hello (désolé pour le délai de réponse, mais si ça peut aider quelqu’un d’autre)
Oui effectivement tu dois ajouter dans le security.yaml :
security.firewalls.main.custom_authenticator: App\Security\GoogleAuthenticator
Et pour retourner une reponse dans le checkAction :
return $this->redirectToRoute('home')
[…] un précédent article, j’avais déjà évoqué ce sujet en prenant comme exemple une authentification Google, on va […]