<?php
namespace App\Controller;
use App\Entity\User;
use App\Services\CurlHttpClient;
use App\Services\SeoManager;
use Exception;
use LogicException;
use stdClass;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
const EXTERNAL_LOGIN_ERROR = 'external_login_error';
class SecurityController extends AbstractController
{
public function login(Request $request, SeoManager $seoManager, AuthenticationUtils $authenticationUtils): Response
{
$seoManager
->setTitleAndDescription('security.login.seo.title', 'security.login.seo.description')
->useLogo();
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('Security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
EXTERNAL_LOGIN_ERROR => $request->query->get(EXTERNAL_LOGIN_ERROR),
]);
}
public function logout(): void
{
throw new LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
/**
* OAuth2 login with Facebook or Google
* Redirect to home if it succeeds
* Redirect to registration page if ti succeeds but user doesn't exist
* Redirect to login in case of fail
*
* @param Request $request
* @param CurlHttpClient $httpClient
* @param EventDispatcherInterface $eventDispatcher
* @param TokenStorageInterface $tokenStorage
* @return Response
*/
public function externalLogin(
Request $request,
CurlHttpClient $httpClient,
EventDispatcherInterface $eventDispatcher,
TokenStorageInterface $tokenStorage
): Response
{
$googleToken = $request->query->get('id_token');
$facebookAccessToken = $request->query->get('access_token');
$data = $googleToken ?
$this->getGoogleData($googleToken, $httpClient) :
$this->getFacebookData($facebookAccessToken, $httpClient);
if ($data !== null && isset($data->email)) {
/** @var User $user */
$user = $this->getDoctrine()
->getRepository(User::class)
->findOneBy(['email' => $data->email]);
if ($user) {
$token = new UsernamePasswordToken($user, $user->getPassword(), "public", $user->getRoles());
$tokenStorage->setToken($token);
$event = new InteractiveLoginEvent($request, $token);
/** @var EventDispatcher $eventDispatcher */
$eventDispatcher->dispatch($event, "security.interactive_login");
return $this->redirectToRoute('whiplay_home');
}
$registrationData = ['email' => $data->email, 'external' => 'true'];
$firstNameKey = 'first_name';
if (property_exists($data, $firstNameKey)) {
$registrationData[$firstNameKey] = $data->first_name;
}
if (property_exists($data, 'given_name')) {
$registrationData[$firstNameKey] = $data->given_name;
}
$lastNameKey = 'last_name';
if (property_exists($data, $lastNameKey)) {
$registrationData[$lastNameKey] = $data->last_name;
}
if (property_exists($data, 'family_name')) {
$registrationData[$lastNameKey] = $data->family_name;
}
if (property_exists($data, 'name')) {
$registrationData['user_name'] = $data->name;
}
return $this->redirectToRoute('app_register', $registrationData);
}
return $this->redirectToRoute('app_login', [EXTERNAL_LOGIN_ERROR => true]);
}
/**
* Fetch Google API with OAuth2 token to get user data
* returns null if it failed
*
* @param $token
* @param $httpClient
* @return mixed|null
*/
private function getGoogleData($token, $httpClient): ?stdClass
{
$data = null;
try {
$url = 'https://oauth2.googleapis.com/tokeninfo?id_token=' . $token;
$response = $httpClient->get($url);
$response = json_decode($response);
if ($response instanceof stdClass && $response->email) {
$data = $response;
}
} catch (Exception $exception) {
$data = null;
}
return $data;
}
/**
* Fetch Facebook API with OAuth2 token to get user data
* returns null if it failed
*
* @param $token
* @param $httpClient
* @return stdClass|null
*/
private function getFacebookData($token, $httpClient): ?stdClass
{
$data = null;
try {
$clientId = $this->getParameter('facebook_client_id');
$clientSecret = $this->getParameter('facebook_client_secret');
$url = 'https://graph.facebook.com/oauth/access_token?client_id=' . $clientId . '&client_secret=' . $clientSecret . '&grant_type=client_credentials';
$response = $httpClient->get($url);
$response = json_decode($response);
$appToken = $response->access_token;
$url = 'https://graph.facebook.com/debug_token?input_token=' . $appToken . '&access_token=' . $token;
$response = $httpClient->get($url);
$response = json_decode($response);
if (isset($response->data) && $response->data->is_valid) {
$url = 'https://graph.facebook.com/me?fields=email,birthday,first_name,last_name&access_token=' . $token;
$response = $httpClient->get($url);
$response = json_decode($response);
if (isset($response->email)) {
$data = new stdClass();
$data->email = $response->email;
$data->pseudo = $response->name ?? '';
$data->firstName = $response->given_name ?? '';
$data->familyName = $response->family_name ?? '';
}
}
} catch (Exception $exception) {
$data = null;
}
return $data;
}
}