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