src/App/Controller/API/AuthController.php line 254

  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controller\API;
  4. use API\Exception\ValidationException;
  5. use App\DTO\APIRequest\AuthDTO;
  6. use App\DTO\Auth\AuthJWTResponse;
  7. use App\DTO\Auth\AuthUserRawData;
  8. use App\Entity\Users;
  9. use App\Security\EmailAuthenticator;
  10. use App\Security\FacebookSignedRequestAuthenticator;
  11. use App\Security\GoogleOAuth2Authenticator;
  12. use App\Service\User\UserLoginService;
  13. use App\Service\User\UsersManagementService;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Gesdinet\JWTRefreshTokenBundle\Generator\RefreshTokenGeneratorInterface;
  16. use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
  17. use KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient;
  18. use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
  19. use League\OAuth2\Client\Token\AccessToken;
  20. use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
  21. use Nelmio\ApiDocBundle\Attribute\Model;
  22. use OpenApi\Attributes as OA;
  23. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  24. use Symfony\Component\HttpFoundation\JsonResponse;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\Response;
  27. use Symfony\Component\Routing\Attribute\Route;
  28. use Symfony\Component\Security\Core\Exception\AuthenticationException;
  29. #[OA\Tag(name'Authentication')]
  30. #[OA\Response(
  31.     response200,
  32.     description'',
  33.     content: new OA\JsonContent(
  34.         ref: new Model(typeAuthJWTResponse::class)
  35.     )
  36. )]
  37. #[OA\Response(
  38.     response403,
  39.     description'Forbidden',
  40. )]
  41. #[Route(path'/auth')]
  42. final class AuthController extends AbstractController
  43. {
  44.     public const REFRESH_TTL 2592000;
  45.     public function __construct(
  46.         private readonly EntityManagerInterface $em,
  47.         private readonly UserLoginService $userLoginService,
  48.     ) {}
  49.     #[OA\QueryParameter(
  50.         name'access_token',
  51.         requiredtrue,
  52.     )]
  53.     #[Route(path'/facebook/verify'name'api.auth.facebook.check'methods: ['GET'])]
  54.     public function getAuthFacebookCheck(
  55.         Request $request,
  56.         ClientRegistry $clientRegistry,
  57.         JWTTokenManagerInterface $jwt,
  58.         UsersManagementService $usersManagementService,
  59.         EntityManagerInterface $em,
  60.         RefreshTokenGeneratorInterface $refreshTokenGenerator,
  61.     ): JsonResponse {
  62.         $accessToken $request->get('access_token');
  63.         /** @var FacebookClient $client */
  64.         $client $clientRegistry->getClient('facebook_main');
  65.         try {
  66.             $t = new AccessToken(
  67.                 [
  68.                     'access_token' => $accessToken,
  69.                 ]
  70.             );
  71.             $facebookUser $client->fetchUserFromToken($t);
  72.         } catch (IdentityProviderException $e) {
  73.             return $this->json(
  74.                 new AuthJWTResponse(
  75.                     AuthJWTResponse::STATUS_ERROR,
  76.                     null,
  77.                     $e->getMessage()
  78.                 ),
  79.                 Response::HTTP_FORBIDDEN
  80.             );
  81.         }
  82.         /** @var Users $user */
  83.         $user $this
  84.             ->em
  85.             ->getRepository(Users::class)
  86.             ->findByFacebook($facebookUser->getId());
  87.         if (!$user) {
  88.             $authUserRawData = (new AuthUserRawData())
  89.                 ->setIdentity($facebookUser->getId())
  90.                 ->setFirstName($facebookUser->getFirstName())
  91.                 ->setLastName($facebookUser->getLastName())
  92.                 ->setAvatar($facebookUser->getPictureUrl())
  93.                 ->setLanguage($request->getLocale());
  94.             $user $usersManagementService->createUser(Users::AUTH_FB$authUserRawData$this->getUser());
  95.         } else {
  96.             try {
  97.                 $avatar $facebookUser->getPictureUrl();
  98.                 if (!empty($avatar)) {
  99.                     $user->getUserData()->setPicture($avatar);
  100.                     $em->flush();
  101.                 }
  102.             } catch (\Throwable $e) {
  103.             }
  104.         }
  105.         $token $jwt->create($user);
  106.         $refreshToken $refreshTokenGenerator->createForUserWithTtl($userself::REFRESH_TTL);
  107.         $this->em->persist($refreshToken);
  108.         $this->em->flush();
  109.         return $this->json(
  110.             new AuthJWTResponse(
  111.                 AuthJWTResponse::STATUS_SUCCESS,
  112.                 $token,
  113.                 $refreshToken->getRefreshToken()
  114.             )
  115.         );
  116.     }
  117.     #[OA\QueryParameter(
  118.         name'access_token',
  119.         requiredtrue,
  120.     )]
  121.     #[OA\QueryParameter(
  122.         name'code',
  123.         requiredfalse,
  124.     )]
  125.     #[OA\QueryParameter(
  126.         name'first_name',
  127.         requiredtrue,
  128.     )]
  129.     #[OA\QueryParameter(
  130.         name'last_name',
  131.         requiredtrue,
  132.     )]
  133.     #[Route(path'/apple/verify'name'api.auth.apple.check'methods: ['GET'])]
  134.     public function getAuthAppleCheck(
  135.         Request $request,
  136.         ClientRegistry $clientRegistry,
  137.         JWTTokenManagerInterface $jwt,
  138.         UsersManagementService $usersManagementService,
  139.         RefreshTokenGeneratorInterface $refreshTokenGenerator,
  140.     ): JsonResponse {
  141.         $accessToken $request->get('access_token');
  142.         $code $request->get('code');
  143.         $firstName = (string) $request->get('first_name');
  144.         $lastName = (string) $request->get('last_name');
  145.         try {
  146.             $appleUser $usersManagementService->decodeAppleAccessToken($accessToken);
  147.         } catch (\Exception $e) {
  148.             return $this->json(
  149.                 new AuthJWTResponse(
  150.                     AuthJWTResponse::STATUS_ERROR,
  151.                     null,
  152.                     $e->getMessage()
  153.                 ),
  154.                 Response::HTTP_FORBIDDEN
  155.             );
  156.         }
  157.         /** @var Users $user */
  158.         $user $this
  159.             ->em
  160.             ->getRepository(Users::class)
  161.             ->findByApple($appleUser['id']);
  162.         if (!$user) {
  163.             $authUserRawData = (new AuthUserRawData())
  164.                 ->setIdentity($appleUser['id'])
  165.                 ->setEmail($appleUser['email'])
  166.                 ->setFirstName($firstName)
  167.                 ->setLastName($lastName);
  168.             $user $usersManagementService->createUser(Users::AUTH_APPLE$authUserRawData$this->getUser());
  169.         }
  170.         $token $jwt->create($user);
  171.         $refreshToken $refreshTokenGenerator->createForUserWithTtl($userself::REFRESH_TTL);
  172.         $this->em->persist($refreshToken);
  173.         $this->em->flush();
  174.         return $this->json(
  175.             new AuthJWTResponse(
  176.                 AuthJWTResponse::STATUS_SUCCESS,
  177.                 $token,
  178.                 $refreshToken->getRefreshToken()
  179.             )
  180.         );
  181.     }
  182.     #[OA\QueryParameter(
  183.         name'access_token',
  184.         requiredtrue,
  185.     )]
  186.     #[Route(path'/google/verify'name'api.auth.google.check'methods: ['GET'])]
  187.     public function getAuthGoogleCheck(
  188.         Request $request,
  189.         ClientRegistry $clientRegistry,
  190.         JWTTokenManagerInterface $jwt,
  191.         UsersManagementService $usersManagementService,
  192.         RefreshTokenGeneratorInterface $refreshTokenGenerator,
  193.     ): JsonResponse {
  194.         $accessToken $request->get('access_token');
  195.         try {
  196.             $googleUser $usersManagementService->decodeGoogleAccessToken($accessToken);
  197.         } catch (\Exception $e) {
  198.             return $this->json(
  199.                 new AuthJWTResponse(
  200.                     AuthJWTResponse::STATUS_ERROR,
  201.                     null,
  202.                     $e->getMessage()
  203.                 ),
  204.                 Response::HTTP_FORBIDDEN
  205.             );
  206.         }
  207.         // $identity $googleUser['id']
  208.         $identity $googleUser['email'];
  209.         /** @var Users $user */
  210.         $user $this
  211.             ->em
  212.             ->getRepository(Users::class)
  213.             ->findByGoogle($identity);
  214.         if (!$user) {
  215.             $authUserRawData = (new AuthUserRawData())
  216.                 ->setIdentity($identity)
  217.                 ->setEmail($googleUser['email'])
  218.                 ->setFirstName($googleUser['first_name'])
  219.                 ->setLastName($googleUser['last_name'])
  220.                 ->setAvatar($googleUser['picture'])
  221.                 ->setLanguage($googleUser['locale']);
  222.             $user $usersManagementService->createUser(Users::AUTH_GOOGLE$authUserRawData$this->getUser());
  223.         }
  224.         $token $jwt->create($user);
  225.         $refreshToken $refreshTokenGenerator->createForUserWithTtl($userself::REFRESH_TTL);
  226.         $this->em->persist($refreshToken);
  227.         $this->em->flush();
  228.         return $this->json(
  229.             new AuthJWTResponse(
  230.                 AuthJWTResponse::STATUS_SUCCESS,
  231.                 $token,
  232.                 $refreshToken->getRefreshToken()
  233.             )
  234.         );
  235.     }
  236.     #[OA\QueryParameter(
  237.         name'access_token',
  238.         requiredtrue,
  239.     )]
  240.     #[Route(path'/huawei/verify'methods: ['GET'])]
  241.     public function getAuthHuaweiCheck(
  242.         Request $request,
  243.         ClientRegistry $clientRegistry,
  244.         JWTTokenManagerInterface $jwt,
  245.         UsersManagementService $usersManagementService,
  246.         RefreshTokenGeneratorInterface $refreshTokenGenerator,
  247.     ): JsonResponse {
  248.         $accessToken $request->get('access_token');
  249.         try {
  250.             $huaweiUser $usersManagementService->decodeHuaweiAccessToken($accessToken);
  251.         } catch (\Exception $e) {
  252.             return $this->json(
  253.                 new AuthJWTResponse(
  254.                     AuthJWTResponse::STATUS_ERROR,
  255.                     null,
  256.                     $e->getMessage()
  257.                 ),
  258.                 Response::HTTP_FORBIDDEN
  259.             );
  260.         }
  261.         // $identity $googleUser['id']
  262.         $identity $huaweiUser['id'];
  263.         /** @var Users $user */
  264.         $user $this
  265.             ->em
  266.             ->getRepository(Users::class)
  267.             ->findByHuawei($identity);
  268.         if (!$user) {
  269.             $authUserRawData = (new AuthUserRawData())
  270.                 ->setIdentity($identity)
  271.                 ->setEmail($huaweiUser['id'].'@huawei.com')
  272.                 ->setFirstName($huaweiUser['first_name'])
  273.                 ->setLastName($huaweiUser['last_name'])
  274.                 ->setLanguage($huaweiUser['locale']);
  275.             $user $usersManagementService->createUser(Users::AUTH_HUAWEI$authUserRawData$this->getUser());
  276.         }
  277.         $token $jwt->create($user);
  278.         $refreshToken $refreshTokenGenerator->createForUserWithTtl($userself::REFRESH_TTL);
  279.         $this->em->persist($refreshToken);
  280.         $this->em->flush();
  281.         return $this->json(
  282.             new AuthJWTResponse(
  283.                 AuthJWTResponse::STATUS_SUCCESS,
  284.                 $token,
  285.                 $refreshToken->getRefreshToken()
  286.             )
  287.         );
  288.     }
  289.     #[OA\QueryParameter(
  290.         name'device_identity',
  291.         requiredtrue,
  292.     )]
  293.     #[Route(path'/guest'methods: ['GET'])]
  294.     public function getAuthGuest(
  295.         Request $request,
  296.         JWTTokenManagerInterface $jwt,
  297.         UsersManagementService $usersManagementService,
  298.         RefreshTokenGeneratorInterface $refreshTokenGenerator,
  299.     ): JsonResponse {
  300.         $deviceIdentity $request->get('device_identity');
  301.         if ($deviceIdentity) {
  302.             $user $usersManagementService->findOrCreateGuestUserByDeviceIdentity($deviceIdentity);
  303.         } else {
  304.             $user $usersManagementService->createGuestUser();
  305.         }
  306.         $token $jwt->create($user);
  307.         $refreshToken $refreshTokenGenerator->createForUserWithTtl($userself::REFRESH_TTL);
  308.         $this->em->persist($refreshToken);
  309.         $this->em->flush();
  310.         return $this->json(
  311.             new AuthJWTResponse(
  312.                 AuthJWTResponse::STATUS_SUCCESS,
  313.                 $token,
  314.                 $refreshToken->getRefreshToken()
  315.             )
  316.         );
  317.     }
  318.     #[OA\RequestBody(
  319.         requiredtrue,
  320.         content: new OA\JsonContent(
  321.             ref: new Model(typeAuthDTO::class)
  322.         )
  323.     )]
  324.     #[Route(path''methods: ['POST'])]
  325.     public function auth(
  326.         AuthDTO $requestDTO,
  327.         Request $request,
  328.         JWTTokenManagerInterface $jwt,
  329.         GoogleOAuth2Authenticator $googleOAuth2Authenticator,
  330.         FacebookSignedRequestAuthenticator $facebookAuthenticator,
  331.         EmailAuthenticator $emailAuthenticator,
  332.         RefreshTokenGeneratorInterface $refreshTokenGenerator,
  333.     ): AuthJWTResponse {
  334.         $request->request->set('type'$requestDTO->type);
  335.         $request->request->set('token'$requestDTO->token);
  336.         $request->request->set('email'$requestDTO->email);
  337.         $request->request->set('password'$requestDTO->password);
  338.         try {
  339.             $passport = match ($requestDTO->type) {
  340.                 'email' => $emailAuthenticator->authenticate($request),
  341.                 'google' => $googleOAuth2Authenticator->authenticate($request),
  342.                 'facebook' => $facebookAuthenticator->authenticate($request),
  343.                 default => throw new AuthenticationException('Invalid type'),
  344.             };
  345.         } catch (AuthenticationException $e) {
  346.             match ($requestDTO->type) {
  347.                 'email' => throw ValidationException::violation('Invalid email or password'$requestDTO->email'email'),
  348.                 default => throw ValidationException::violation('Invalid credentials'$requestDTO->token'token'),
  349.             };
  350.         }
  351.         $user $passport->getUser();
  352.         if (!$user instanceof Users) {
  353.             throw $this->createAccessDeniedException();
  354.         }
  355.         $token $jwt->create($user);
  356.         $refreshToken $refreshTokenGenerator->createForUserWithTtl($userself::REFRESH_TTL);
  357.         $this->em->persist($refreshToken);
  358.         $this->em->flush();
  359.         $this->userLoginService->callLoginRoutines($user$request);
  360.         return new AuthJWTResponse(
  361.             AuthJWTResponse::STATUS_SUCCESS,
  362.             $token,
  363.             $refreshToken->getRefreshToken()
  364.         );
  365.     }
  366. }