<?php
namespace Webkul\UVDesk\CoreFrameworkBundle\Controller;
use League\OAuth2\Client\Provider\Google;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class GoogleSSO extends AbstractController
{
private string $googleClientId;
private string $googleClientSecret;
private string $googleRedirectUri;
private \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage;
private \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher;
private \Doctrine\ORM\EntityManagerInterface $entityManager;
private \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider;
public function __construct(
string $googleClientId,
string $googleClientSecret,
string $googleRedirectUri,
\Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage,
\Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher,
\Doctrine\ORM\EntityManagerInterface $entityManager,
\Symfony\Component\Security\Core\User\UserProviderInterface $userProvider
) {
$this->googleClientId = $googleClientId;
$this->googleClientSecret = $googleClientSecret;
$this->googleRedirectUri = $googleRedirectUri;
$this->tokenStorage = $tokenStorage;
$this->eventDispatcher = $eventDispatcher;
$this->entityManager = $entityManager;
$this->userProvider = $userProvider;
}
private function getGoogleProvider(): Google
{
return new Google([
'clientId' => $this->googleClientId,
'clientSecret' => $this->googleClientSecret,
'redirectUri' => $this->googleRedirectUri,
'scopes' => ['openid', 'email', 'profile'],
]);
}
/**
* STEP 1: Redirect user to Google
* Route name example: helpdesk_member_google_login
*/
public function googlelogin(SessionInterface $session): RedirectResponse
{
$provider = $this->getGoogleProvider();
// Generate authorization URL
$authUrl = $provider->getAuthorizationUrl();
// Save state in session for CSRF protection
$session->set('oauth2state', $provider->getState());
// Redirect to Google
return new RedirectResponse($authUrl);
}
/**
* STEP 2: Google redirects back here with ?code= and ?state=
* Route name example: helpdesk_member_google_callback
*/
public function googlecallback(
Request $request,
SessionInterface $session
): Response
{
$provider = $this->getGoogleProvider();
// 1) Handle Google error
if ($request->query->has('error')) {
$error = $request->query->get('error');
return new Response(
'Got error from Google: ' . htmlspecialchars($error, ENT_QUOTES, 'UTF-8'),
400
);
}
// 2) Ensure we have an authorization code
if (!$request->query->has('code')) {
return new Response('No authorization code returned from Google.', 400);
}
// 3) Validate state (CSRF protection)
$state = $request->query->get('state');
$sessionState = $session->get('oauth2state');
if (empty($state) || $state !== $sessionState) {
$session->remove('oauth2state');
return new Response('Invalid state – possible CSRF.', 400);
}
try {
// 4) Exchange code for access token
$token = $provider->getAccessToken('authorization_code', [
'code' => $request->query->get('code'),
]);
// 5) Get user details from Google
$owner = $provider->getResourceOwner($token);
$googleEmail = $owner->getEmail();
// 6) Load local user via UserProvider (handles roles, instances, validation)
try {
$user = $this->userProvider->loadUserByUsername($googleEmail);
} catch (\Symfony\Component\Security\Core\Exception\UsernameNotFoundException $e) {
return new Response("User with email $googleEmail not found or not active.", 403);
}
// 7) Programmatic Login
$firewallName = 'back_support';
// Roles are already set by UserProvider::loadUserByUsername
$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken(
$user,
null,
$firewallName,
$user->getRoles()
);
// Set token in storage
$this->tokenStorage->setToken($token);
// Set session attribute for serialization
$sessionKey = '_security_' . $firewallName;
$serializedToken = serialize($token);
$session->set($sessionKey, $serializedToken);
$session->save();
// DEBUG LOGGING
error_log("GoogleSSO: Logged in user {$user->getEmail()}");
error_log("GoogleSSO: Roles: " . implode(',', $user->getRoles()));
// Dispatch interactive login event
$event = new \Symfony\Component\Security\Http\Event\InteractiveLoginEvent($request, $token);
$this->eventDispatcher->dispatch($event, 'security.interactive_login');
// 8) Redirect
return $this->redirect('/public/en/member/dashboard');
} catch (\Exception $e) {
return new Response('Something went wrong: ' . $e->getMessage(), 500);
}
}
}