feat: remove legacy UserToken based auth
This commit is contained in:
@@ -79,10 +79,6 @@ services:
|
||||
arguments:
|
||||
- '%env(APP_ENV)%'
|
||||
|
||||
App\Service\Auth\AuthService:
|
||||
arguments:
|
||||
- '%env(APP_SECRET)%'
|
||||
|
||||
App\Service\LocalizationService:
|
||||
arguments:
|
||||
- '%kernel.default_locale%'
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\ApiResource\User;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\State\Options;
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\Dto\UserTokenLoginDto;
|
||||
use App\Entity\User\UserToken;
|
||||
use App\State\ApiResourceStateProcessor;
|
||||
use App\State\ApiResourceStateProvider;
|
||||
use App\State\User\UserTokenLoginProcessor;
|
||||
|
||||
/**
|
||||
* UserTokens authenticate requests on behalf of the User who owns them.\
|
||||
* \
|
||||
* When a UserToken is created v4 generates a SHA-256 hash that is unique for each UserToken.
|
||||
* The value of a UserToken comes preceded by a 4-digit-length prefix based on the type of token it is.
|
||||
*/
|
||||
#[API\ApiResource(
|
||||
shortName: 'UserToken',
|
||||
stateOptions: new Options(entityClass: UserToken::class),
|
||||
provider: ApiResourceStateProvider::class,
|
||||
processor: ApiResourceStateProcessor::class,
|
||||
)]
|
||||
#[API\Post(input: UserTokenLoginDto::class, processor: UserTokenLoginProcessor::class)]
|
||||
#[API\Get(security: 'is_granted("USER_OWNED", object)')]
|
||||
#[API\Delete(security: 'is_granted("USER_OWNED", object)')]
|
||||
class UserTokenApiResource
|
||||
{
|
||||
#[API\ApiProperty(writable: false, identifier: true)]
|
||||
public int $id;
|
||||
|
||||
/**
|
||||
* The User token itself.
|
||||
*/
|
||||
#[API\ApiProperty(writable: false)]
|
||||
public string $token;
|
||||
|
||||
/**
|
||||
* The User who this token grants access as.
|
||||
*/
|
||||
#[API\ApiProperty(writable: false)]
|
||||
public UserApiResource $owner;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Dto;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* UserTokens are created via a login flow where v4:
|
||||
* 1. Receives the User credentials
|
||||
* 2. Validates them
|
||||
* 3. Generates the UserToken for the authenticated User
|
||||
*/
|
||||
final class UserTokenLoginDto
|
||||
{
|
||||
/**
|
||||
* The identifier (email, handle) of the User to be authenticated.
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
public readonly string $identifier;
|
||||
|
||||
/**
|
||||
* The password of the User to be authenticated.
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
public readonly string $password;
|
||||
|
||||
public function __construct(string $identifier, string $password)
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
$this->password = $password;
|
||||
}
|
||||
}
|
||||
@@ -72,12 +72,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Account
|
||||
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: Project::class, cascade: ['persist'])]
|
||||
private Collection $projects;
|
||||
|
||||
/**
|
||||
* The UserTokens owned by this User. Owner only property.
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: UserToken::class, orphanRemoval: true)]
|
||||
private Collection $tokens;
|
||||
|
||||
#[ORM\OneToOne(mappedBy: 'user', cascade: ['persist', 'remove'])]
|
||||
private ?Person $person = null;
|
||||
|
||||
@@ -128,7 +122,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Account
|
||||
$this->accounting = Accounting::of($this);
|
||||
|
||||
$this->projects = new ArrayCollection();
|
||||
$this->tokens = new ArrayCollection();
|
||||
$this->person = Person::for($this);
|
||||
|
||||
$this->emailConfirmed = false;
|
||||
@@ -236,36 +229,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Account
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, UserToken>
|
||||
*/
|
||||
public function getTokens(): Collection
|
||||
{
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
public function addToken(UserToken $token): static
|
||||
{
|
||||
if (!$this->tokens->contains($token)) {
|
||||
$this->tokens->add($token);
|
||||
$token->setOwner($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeToken(UserToken $token): static
|
||||
{
|
||||
if ($this->tokens->removeElement($token)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($token->getOwner() === $this) {
|
||||
$token->setOwner(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
return $this->person;
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity\User;
|
||||
|
||||
use App\Entity\DateCreatedTrait;
|
||||
use App\Entity\UserOwnedInterface;
|
||||
use App\Entity\UserOwnedTrait;
|
||||
use App\Mapping\Provider\EntityMapProvider;
|
||||
use App\Repository\User\UserTokenRepository;
|
||||
use AutoMapper\Attribute\MapProvider;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* UserTokens authenticate requests on behalf of the User who owns them.\
|
||||
* \
|
||||
* When a UserToken is created v4 generates a SHA-256 hash that is unique for that Token and the User it represents.
|
||||
* The value of a token comes preceded by a 4-digit-length prefix based on the type of token it is.\
|
||||
* \
|
||||
* `oat_` means the token was created via an OAuth flow.\
|
||||
* `pat_` means the token was created via a login flow.
|
||||
*/
|
||||
#[MapProvider(EntityMapProvider::class)]
|
||||
#[ORM\Entity(repositoryClass: UserTokenRepository::class)]
|
||||
class UserToken implements UserOwnedInterface
|
||||
{
|
||||
use DateCreatedTrait;
|
||||
use UserOwnedTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* The value to be used in the Authorization header.
|
||||
*/
|
||||
#[ORM\Column(length: 68)]
|
||||
private ?string $token = null;
|
||||
|
||||
/**
|
||||
* The User on behalf of which this UserToken authenticates.
|
||||
*/
|
||||
#[ORM\ManyToOne(inversedBy: 'tokens')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?User $owner = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getToken(): ?string
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
public function setToken(string $token): static
|
||||
{
|
||||
$this->token = $token;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use ApiPlatform\Symfony\EventListener\EventPriorities;
|
||||
use App\ApiResource\User\UserTokenApiResource;
|
||||
use App\Service\Auth\AuthService;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\ViewEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
final class AuthorizationTokenSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private AuthService $authService,
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
KernelEvents::VIEW => ['onView', EventPriorities::POST_WRITE],
|
||||
KernelEvents::RESPONSE => ['onResponse', EventPriorities::POST_RESPOND],
|
||||
];
|
||||
}
|
||||
|
||||
public function onView(ViewEvent $event): void
|
||||
{
|
||||
$resourceClass = $event->getRequest()->attributes->get('_api_resource_class');
|
||||
|
||||
if ($resourceClass !== UserTokenApiResource::class) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event->getRequest()->attributes->set('authTokenMethod', $event->getRequest()->getMethod());
|
||||
|
||||
if ($event->getRequest()->getMethod() === Request::METHOD_POST) {
|
||||
$token = $event->getControllerResult();
|
||||
|
||||
$event->getRequest()->attributes->set('authTokenValue', $token->token);
|
||||
}
|
||||
}
|
||||
|
||||
public function onResponse(ResponseEvent $event): void
|
||||
{
|
||||
$token = $event->getRequest()->attributes->get('authTokenValue');
|
||||
$method = $event->getRequest()->attributes->get('authTokenMethod');
|
||||
|
||||
if (!$method) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case Request::METHOD_DELETE:
|
||||
$event->getResponse()->headers->clearCookie(AuthService::AUTH_COOKIE_NAME);
|
||||
break;
|
||||
case Request::METHOD_POST:
|
||||
$event->getResponse()->headers->setCookie($this->authService->generateCookie($token));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ trait RedocExtensionsTrait
|
||||
'name' => 'Users',
|
||||
'tags' => [
|
||||
'User',
|
||||
'UserToken',
|
||||
'Person',
|
||||
'Organization',
|
||||
],
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository\User;
|
||||
|
||||
use App\Entity\User\UserToken;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<UserToken>
|
||||
*
|
||||
* @method UserToken|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method UserToken|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method UserToken[] findAll()
|
||||
* @method UserToken[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class UserTokenRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, UserToken::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return UserToken[] Returns an array of UserToken objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('a')
|
||||
// ->andWhere('a.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('a.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?UserToken
|
||||
// {
|
||||
// return $this->createQueryBuilder('a')
|
||||
// ->andWhere('a.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use App\Repository\User\UserTokenRepository;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
|
||||
class AccessTokenHandler implements AccessTokenHandlerInterface
|
||||
{
|
||||
public function __construct(private UserTokenRepository $userTokenRepository) {}
|
||||
|
||||
public function getUserBadgeFrom(#[\SensitiveParameter] string $accessToken): UserBadge
|
||||
{
|
||||
$token = $this->userTokenRepository->findOneBy(['token' => $accessToken]);
|
||||
|
||||
if (!$token) {
|
||||
throw new BadCredentialsException();
|
||||
}
|
||||
|
||||
return new UserBadge($token->getOwner()->getUserIdentifier());
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use App\Service\Auth\AuthService;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
|
||||
|
||||
/**
|
||||
* @see https://symfony.com/doc/current/security/custom_authenticator.html
|
||||
*/
|
||||
class CookieAuthenticator extends AbstractAuthenticator
|
||||
{
|
||||
public function __construct(
|
||||
private AccessTokenHandler $accessTokenHandler,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Called on every request to decide if this authenticator should be
|
||||
* used for the request. Returning `false` will cause this authenticator
|
||||
* to be skipped.
|
||||
*/
|
||||
public function supports(Request $request): ?bool
|
||||
{
|
||||
return $request->cookies->has(AuthService::AUTH_COOKIE_NAME);
|
||||
}
|
||||
|
||||
public function authenticate(Request $request): Passport
|
||||
{
|
||||
$token = $request->cookies->get(AuthService::AUTH_COOKIE_NAME);
|
||||
|
||||
if (!$token) {
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
|
||||
return new SelfValidatingPassport($this->accessTokenHandler->getUserBadgeFrom($token));
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
|
||||
{
|
||||
return new JsonResponse([
|
||||
'message' => strtr($exception->getMessageKey(), $exception->getMessageData()),
|
||||
], Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Auth;
|
||||
|
||||
use App\Entity\User\User;
|
||||
use App\Entity\User\UserToken;
|
||||
use App\Repository\User\UserRepository;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
|
||||
class AuthService
|
||||
{
|
||||
public const AUTH_COOKIE_NAME = 'authToken';
|
||||
public const AUTH_COOKIE_TTL = 86400;
|
||||
|
||||
private const TOKEN_HASH_ALGO = 'sha256';
|
||||
|
||||
private array $config;
|
||||
|
||||
public function __construct(
|
||||
private string $appSecret,
|
||||
private Security $security,
|
||||
private UserRepository $userRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return array{CORS_ALLOW_ORIGIN: string, SESSION_LIFETIME: int}
|
||||
*/
|
||||
public function getConfig(): array
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
private function getCookieTtl(): \DateTimeInterface
|
||||
{
|
||||
return (new \DateTime())->add(new \DateInterval(\sprintf('PT%dS', AuthService::AUTH_COOKIE_TTL)));
|
||||
}
|
||||
|
||||
public function generateCookie(string $token): Cookie
|
||||
{
|
||||
return new Cookie(
|
||||
name: AuthService::AUTH_COOKIE_NAME,
|
||||
value: $token,
|
||||
expire: $this->getCookieTtl(),
|
||||
path: '/',
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
sameSite: 'Strict'
|
||||
);
|
||||
}
|
||||
|
||||
public function generateUserToken(User $user, AuthTokenType $type): UserToken
|
||||
{
|
||||
$token = new UserToken();
|
||||
|
||||
$token->setOwner($user);
|
||||
$token->setToken(sprintf('%s%s', $type->value, hash(
|
||||
self::TOKEN_HASH_ALGO,
|
||||
join('', [
|
||||
microtime(true),
|
||||
$this->appSecret,
|
||||
random_bytes(32),
|
||||
$user->getUserIdentifier(),
|
||||
])
|
||||
)));
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
$loggedInUser = $this->security->getUser();
|
||||
|
||||
if (!$loggedInUser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->userRepository->findOneBy(
|
||||
['handle' => $loggedInUser->getUserIdentifier()]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ use App\Dto\ProjectUpdationDto;
|
||||
use App\Entity\Project\Project;
|
||||
use App\Entity\Project\ProjectCalendar;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Service\Auth\AuthService;
|
||||
use App\State\EntityStateProcessor;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
|
||||
class ProjectStateProcessor implements ProcessorInterface
|
||||
@@ -19,7 +19,7 @@ class ProjectStateProcessor implements ProcessorInterface
|
||||
public function __construct(
|
||||
private EntityStateProcessor $entityStateProcessor,
|
||||
private AutoMapper $autoMapper,
|
||||
private AuthService $authService,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ class ProjectStateProcessor implements ProcessorInterface
|
||||
/** @var Project */
|
||||
$project = $this->autoMapper->map($data, Project::class);
|
||||
|
||||
$owner = $this->authService->getUser();
|
||||
$owner = $this->security->getUser();
|
||||
|
||||
if (!$owner) {
|
||||
throw new AuthenticationException();
|
||||
|
||||
@@ -8,8 +8,8 @@ use App\ApiResource\Project\RewardClaimApiResource;
|
||||
use App\Dto\RewardClaimCreationDto;
|
||||
use App\Entity\Project\RewardClaim;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Service\Auth\AuthService;
|
||||
use App\State\EntityStateProcessor;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
|
||||
class RewardClaimStateProcessor implements ProcessorInterface
|
||||
@@ -17,7 +17,7 @@ class RewardClaimStateProcessor implements ProcessorInterface
|
||||
public function __construct(
|
||||
private EntityStateProcessor $entityStateProcessor,
|
||||
private AutoMapper $autoMapper,
|
||||
private AuthService $authService,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ class RewardClaimStateProcessor implements ProcessorInterface
|
||||
$claim = $this->autoMapper->map($data, RewardClaim::class);
|
||||
|
||||
if ($data instanceof RewardClaimCreationDto) {
|
||||
$owner = $this->authService->getUser();
|
||||
$owner = $this->security->getUser();
|
||||
|
||||
if (!$owner) {
|
||||
throw new AuthenticationException();
|
||||
|
||||
@@ -7,16 +7,16 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\ApiResource\Project\SupportApiResource;
|
||||
use App\Entity\Project\Support;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Service\Auth\AuthService;
|
||||
use App\State\EntityStateProcessor;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
|
||||
class SupportStateProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EntityStateProcessor $entityStateProcessor,
|
||||
private AuthService $authService,
|
||||
private Security $security,
|
||||
private AutoMapper $autoMapper,
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
@@ -28,7 +28,8 @@ class SupportStateProcessor implements ProcessorInterface
|
||||
*/
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
|
||||
{
|
||||
$user = $this->authService->getUser();
|
||||
$user = $this->security->getUser();
|
||||
|
||||
if (!$user) {
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use ApiPlatform\State\ProcessorInterface;
|
||||
use App\ApiResource\Project\UpdateApiResource;
|
||||
use App\Entity\Project\Update;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Service\Auth\AuthService;
|
||||
use App\State\EntityStateProcessor;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
|
||||
class UpdateStateProcessor implements ProcessorInterface
|
||||
@@ -16,7 +16,7 @@ class UpdateStateProcessor implements ProcessorInterface
|
||||
public function __construct(
|
||||
private EntityStateProcessor $entityStateProcessor,
|
||||
private AutoMapper $autoMapper,
|
||||
private AuthService $authService,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,8 @@ class UpdateStateProcessor implements ProcessorInterface
|
||||
/** @var Update */
|
||||
$update = $this->autoMapper->map($data, Update::class);
|
||||
|
||||
$user = $this->authService->getUser();
|
||||
$user = $this->security->getUser();
|
||||
|
||||
if (!$user) {
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\State\User;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\ApiResource\User\UserTokenApiResource;
|
||||
use App\Dto\UserTokenLoginDto;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Repository\User\UserRepository;
|
||||
use App\Service\Auth\AuthService;
|
||||
use App\Service\Auth\AuthTokenType;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
|
||||
class UserTokenLoginProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private AuthService $authService,
|
||||
private UserRepository $userRepository,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private UserPasswordHasherInterface $userPasswordHasher,
|
||||
private AutoMapper $autoMapper,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param UserTokenLoginDto $data
|
||||
*
|
||||
* @return UserTokenApiResource
|
||||
*/
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
|
||||
{
|
||||
$user = $this->userRepository->findOneByIdentifier($data->identifier);
|
||||
|
||||
if (!$user) {
|
||||
throw new HttpException(Response::HTTP_NOT_FOUND, sprintf("The user '%s' does not exist.", $data->identifier));
|
||||
}
|
||||
|
||||
if (!$this->userPasswordHasher->isPasswordValid($user, $data->password)) {
|
||||
throw new HttpException(Response::HTTP_UNAUTHORIZED, sprintf('The password could not be validated'));
|
||||
}
|
||||
|
||||
$token = $this->authService->generateUserToken($user, AuthTokenType::Personal);
|
||||
|
||||
$this->entityManager->persist($token);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $this->autoMapper->map($token, UserTokenApiResource::class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user