Skip to content

Commit

Permalink
Issue 00 - rework google auth process
Browse files Browse the repository at this point in the history
Body : goals are to prevent erreor on first auth and to clean the whole process
  • Loading branch information
ThomasLdev committed Apr 6, 2024
1 parent b4188ec commit d427743
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 127 deletions.
Binary file added public/icons/instabot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions src/Controller/BaseController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/**
* @author ThomasLdev
*/

declare(strict_types=1);

namespace App\Controller;

use App\Entity\User;
use App\Entity\UserSettings;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Contracts\Translation\TranslatorInterface;

class BaseController extends AbstractController
{
protected const LOGIN_ROUTE = 'app_login';
protected const INDEX_ROUTE = 'app_index';
protected const SETTINGS_ROUTE = 'app_settings';

public function __construct(
private readonly TranslatorInterface $translator,
) {
}

public function flashOnRedirect(string $type, string $message, string $route): RedirectResponse
{
$this->addFlash($type, $this->translator->trans($message));

return $this->redirectToRoute($route);
}

public function getAppUser(): User
{
$user = $this->getUser();

if (false === $user instanceof User) {
$this->flashOnRedirect('error', 'errors.controller.user.not_logged',self::LOGIN_ROUTE);
}

return $user;
}

public function getAppUserSettings(User $user): UserSettings
{
$settings = $user->getSettings();

if (null === $settings) {
$this->flashOnRedirect(
'error',
'errors.controller.user.no_settings',
self::SETTINGS_ROUTE
);
}

return $settings;
}
}
46 changes: 5 additions & 41 deletions src/Controller/DriveFilesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,15 @@

namespace App\Controller;

use App\Entity\User;
use App\Service\Google\Drive\GoogleDriveClientService;
use Google\Service\Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Translation\TranslatorInterface;

class DriveFilesController extends AbstractController
class DriveFilesController extends BaseController
{
private const LOGIN_ROUTE = 'app_login';
private const SETTINGS_ROUTE = 'app_settings';

public function __construct(
private readonly TranslatorInterface $translator,
private readonly LoggerInterface $logger,
) {
}

/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
Expand All @@ -39,39 +25,17 @@ public function __construct(
public function index(
GoogleDriveClientService $driveService,
): Response|RedirectResponse {
$user = $this->getUser();
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

if (false === $user instanceof User) {
return $this->redirectOnError('errors.controller.user.not_logged', self::LOGIN_ROUTE);
}

$settings = $user->getSettings();

if (null === $settings) {
return $this->redirectOnError('errors.controller.user.no_settings', self::SETTINGS_ROUTE);
}

try {
$response = $driveService->getFilesForUser($settings);
} catch (Exception $e) {
$this->logger->error($e->getMessage());

return $this->redirectOnError('errors.drive.general', self::SETTINGS_ROUTE);
}
$settings = $this->getAppUserSettings($this->getAppUser());
$response = $driveService->getFilesForUser($settings);

if (false === $response->getSuccess()) {
return $this->redirectOnError($response->getMessage(), self::SETTINGS_ROUTE);
return $this->flashOnRedirect('error', $response->getMessage(), self::SETTINGS_ROUTE);
}

return $this->render('drive_files/index.html.twig', [
'files' => $response->getFiles(),
]);
}

private function redirectOnError(string $message, string $route): RedirectResponse
{
$this->addFlash('error', $this->translator->trans($message));

return $this->redirectToRoute($route);
}
}
116 changes: 46 additions & 70 deletions src/Controller/GoogleAuthorizeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,44 @@

namespace App\Controller;

use App\Entity\User;
use App\Entity\UserSettings;
use App\Service\Google\GoogleClientService;
use App\Service\Security\EncryptionService;
use App\Service\Google\OAuth\GoogleOAuthTokenService;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Random\RandomException;
use SodiumException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;

class GoogleAuthorizeController extends AbstractController
class GoogleAuthorizeController extends BaseController
{
#[Route('/google/authorize-request', name: 'app_google_authorize_request')]
public function index(GoogleClientService $clientService): RedirectResponse
{
$user = $this->getUser();
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

if (false === $user instanceof User) {
$this->addFlash('error', 'errors.controller.user.not_logged');

return $this->redirectToRoute('app_index');
}

/** @var UserSettings $settings */
$settings = $user->getSettings();
$client = null;
$settings = $this->getAppUserSettings($this->getAppUser());

try {
$client = $clientService->getClientForUser($settings);
} catch (NotFoundExceptionInterface | ContainerExceptionInterface | Exception $e) {
$this->addFlash('error', 'errors.controller.google.authorization_failed');

return $this->redirectToRoute('app_settings');
$this->flashOnRedirect(
'error',
'errors.controller.google.authorization_failed',
BaseController::SETTINGS_ROUTE
);
}

if (null === $client) {
$this->addFlash('error', 'errors.controller.google.authorization_failed');

return $this->redirectToRoute('app_settings');
$this->flashOnRedirect(
'error',
'errors.controller.google.authorization_failed',
BaseController::SETTINGS_ROUTE
);
}

return $this->redirect($client->createAuthUrl());
Expand All @@ -61,72 +56,53 @@ public function index(GoogleClientService $clientService): RedirectResponse
* @throws SodiumException
*/
#[Route('/google/authorize-response', name: 'app_google_authorize_response')]
public function response(
Request $request,
EncryptionService $tokenService,
EntityManagerInterface $entityManager
): RedirectResponse {
public function response(Request $request, GoogleOAuthTokenService $tokenService): RedirectResponse
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

$settings = $this->getAppUserSettings($this->getAppUser());
$authCode = $request->query->get('code');

if (false === is_string($authCode)) {
$this->addFlash('error', 'errors.controller.google.no_code');
$this->flashOnRedirect(
'error',
'errors.controller.google.no_code',
BaseController::SETTINGS_ROUTE
);
}

return $this->redirectToRoute('app_settings');
$response = $tokenService->storeAuthCodeForUser($settings, $authCode);

if (false === $response->getSuccess()) {
return $this->flashOnRedirect(
'error',
'errors.controller.google.authorization_failed',
BaseController::INDEX_ROUTE
);
}

return $this->storeAuthCodeForUser($authCode, $tokenService, $entityManager);
return $this->flashOnRedirect(
'success',
'errors.controller.google.authorization_success',
BaseController::INDEX_ROUTE
);
}

#[Route('/google/revoke-access', name: 'app_google_revoke_access')]
public function revokeAuthCodeForUser(EntityManagerInterface $entityManager): RedirectResponse
{
$user = $this->getUser();

if (false === $user instanceof User) {
$this->addFlash('error', 'errors.controller.user.not_logged');

return $this->redirectToRoute('app_index');
}

/** @var UserSettings $settings */
$settings = $user->getSettings();
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

$settings = $this->getAppUserSettings($this->getAppUser());
$settings->setGoogleDriveAuthCode(null);
$settings->setGoogleDriveToken(null);

$this->addFlash('success', 'form.update.token.revoke');

$entityManager->flush();

return $this->redirectToRoute('app_settings');
}

/**
* @throws RandomException
* @throws SodiumException
*/
private function storeAuthCodeForUser(
string $authCode,
EncryptionService $tokenService,
EntityManagerInterface $entityManager
): RedirectResponse {
$user = $this->getUser();

if (false === $user instanceof User) {
$this->addFlash('error', 'errors.controller.user.not_logged');

return $this->redirectToRoute('app_index');
}

/** @var UserSettings $settings */
$settings = $user->getSettings();

$settings->setGoogleDriveAuthCode($tokenService->encrypt($authCode));

$this->addFlash('success', 'form.update.token.granted');

$entityManager->flush();

return $this->redirectToRoute('app_settings');
return $this->flashOnRedirect(
'success',
'form.update.token.revoke',
BaseController::SETTINGS_ROUTE
);
}
}
18 changes: 7 additions & 11 deletions src/Service/Google/GoogleClientService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace App\Service\Google;

use App\Entity\UserSettings;
use App\Model\GoogleClientResponse;
use App\Service\Google\OAuth\GoogleOAuthTokenService;
use App\Service\Security\EncryptionService;
use Exception;
Expand Down Expand Up @@ -38,7 +39,7 @@ public function __construct(
* @throws ContainerExceptionInterface
* @throws Exception
*/
public function getClientForUser(UserSettings $userSettings): ?Client
public function getClientForUser(UserSettings $userSettings): Client
{
$client = new Client();

Expand All @@ -47,24 +48,19 @@ public function getClientForUser(UserSettings $userSettings): ?Client
$authCode = $userSettings->getGoogleDriveAuthCode();
$accessToken = $userSettings->getGoogleDriveToken();

// first time authorization
if (!$accessToken && !$authCode) {
return $client;
}

/** @var string $authCode */
$response = $this->OAuthService->getToken($accessToken, $authCode, $client, $userSettings);
$authResponse = $this->OAuthService->getAccessToken($userSettings, $client);

if (false === $response->getSuccess() || '' === $response->getAccessToken()) {
// unsuccessful token retrieval or getting a new token
if (false === $authResponse->getSuccess() || !$authResponse->getAccessToken()) {
return $client;
}

$token = $this->encryptionService->decrypt($response->getAccessToken());

if (null === $token) {
return null;
}

$client->setAccessToken($token);
$client->setAccessToken($this->encryptionService->decrypt($authResponse->getAccessToken()));

return $client;
}
Expand Down
Loading

0 comments on commit d427743

Please sign in to comment.