<?php
namespace App\Controller\Frontend;
use App\Entity\User\AccessToken;
use App\Exception\RefreshTokenExpiredException;
use App\Service\AccessTokenService;
use App\ViewModel\Frontend\AccessTokenViewModel;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Annotation\Security;
use OpenApi\Annotations as OA;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\NotNull;
class SecurityController extends AbstractController {
/**
* @var AccessTokenService
*/
private $accessTokenService;
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* @var FormFactoryInterface
*/
private $formFactory;
/**
* SecurityController constructor.
*
* @param AccessTokenService $accessTokenService
* @param EntityManagerInterface $entityManager
* @param FormFactoryInterface $formFactory
*/
public function __construct(
AccessTokenService $accessTokenService,
EntityManagerInterface $entityManager,
FormFactoryInterface $formFactory
) {
$this->accessTokenService = $accessTokenService;
$this->entityManager = $entityManager;
$this->formFactory = $formFactory;
}
/**
* Perform user login
*
* @Route(path="/login", methods={"POST"})
* @Security(name=null)
* @OA\RequestBody(
* @OA\JsonContent(
* type="object",
* @OA\Property(property="username", type="string", nullable=false),
* @OA\Property(property="password", type="string", nullable=false)
* )
* )
* @OA\Response(
* response=200,
* description="The access token",
* @Model(type=AccessTokenViewModel::class)
* )
* @OA\Response(
* response=401,
* description="Authentication failed",
* @OA\JsonContent(
* type="object",
* @OA\Property(property="error", type="string")
* )
* )
* @throws Exception
*/
public function loginAction() {
$user = $this->getUser();
$accessToken = $this->accessTokenService->createAccessToken($user);
$accessTokenViewModel = AccessTokenViewModel::create($accessToken);
return $this->json($accessTokenViewModel);
}
/**
* Refresh an expired accessToken with the refreshToken
*
* @Route(path="/token", methods={"POST"})
* @param Request $request
*
* @return JsonResponse
* @throws RefreshTokenExpiredException
* @throws Exception
*
* @OA\RequestBody(
* @OA\JsonContent(
* @OA\Property(property="refreshToken", type="string", nullable="false")
* )
* )
* @OA\Response(
* response="200",
* description="Refreshed token",
* @Model(type=AccessTokenViewModel::class)
* )
* @OA\Response(
* response="400",
* ref="#/components/schemas/BadRequestError"
* )
* @OA\Response(
* response="404",
* ref="#/components/schemas/NotFoundError"
* )
*/
public function refreshTokenAction(Request $request) {
$form = $this->formFactory
->create(FormType::class)
->add(
'refreshToken',
TextType::class,
[
'constraints' => [new NotNull()],
]
);
$form->submit(json_decode($request->getContent(), true));
if(!$form->isSubmitted() || !$form->isValid()) {
throw new BadRequestHttpException();
}
$refreshToken = $form->get('refreshToken')->getData();
$accessToken = $this->entityManager
->getRepository(AccessToken::class)
->findByRefreshToken($refreshToken);
if($accessToken === null) {
throw $this->createNotFoundException();
}
$accessToken = $this->accessTokenService
->refreshAccessToken($accessToken);
$accessTokenViewModel = AccessTokenViewModel::create($accessToken);
return $this->json($accessTokenViewModel);
}
}