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 […]