<?php
namespace App\Security;
use App\Entity\User;
use App\Model\NamecheapUser;
use App\Repository\UserRepository;
use App\Traits\RouteGeneratorTrait;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Client\OAuth2ClientInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class NamecheapAuthenticator extends OAuth2Authenticator implements AuthenticationEntrypointInterface
{
use RouteGeneratorTrait;
public function __construct(
private readonly ClientRegistry $clientRegistry,
private readonly EntityManagerInterface $entityManager,
private readonly UserRepository $userRepository,
private readonly RouterInterface $router,
protected readonly UrlGeneratorInterface $generator
) {
}
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'oauth_callback';
}
public function authenticate(Request $request): Passport
{
$client = $this->getClient();
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) {
/** @var NamecheapUser $namecheapUser */
$namecheapUser = $client->fetchUserFromToken($accessToken);
$userNickname = $namecheapUser->getNickname();
$existingUser = $this->userRepository->findForAuthenticator($userNickname, $_SERVER['USER']);
if ($existingUser) {
$existingUser->setAccessToken($namecheapUser->getAccessToken());
$existingUser->setNcLogin($userNickname);
$this->entityManager->flush();
return $existingUser;
}
$user = new User();
$user->setName($_SERVER['USER']);
$user->setNcLogin($userNickname);
$user->setAccessToken($namecheapUser->getAccessToken());
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$targetUrl = $this->getUrlByName('home');
return new RedirectResponse($targetUrl);
}
public function getUser($credentials, UserProviderInterface $userProvider): UserInterface
{
return $userProvider->loadUserByIdentifier($this->getClient()->fetchUserFromToken($credentials)->getId());
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
/**
* Called when authentication is needed, but it's not sent.
* This redirects to the 'login'.
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse(
$this->getUrlByName('oauth_connect'), // might be the site, where users choose their oauth provider
Response::HTTP_TEMPORARY_REDIRECT
);
}
private function getClient(): OAuth2ClientInterface
{
return $this->clientRegistry->getClient('namecheap');
}
}