| Current Path : /var/www/v3.cesa.co.za/src/UserBundle/Security/ |
| Current File : /var/www/v3.cesa.co.za/src/UserBundle/Security/OAuth2Authenticator.php |
<?php
namespace App\UserBundle\Security;
use League\OAuth2\Server\ResourceServer;
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\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
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 App\UserBundle\Entity\UserList;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Http\Message\ServerRequestInterface;
class OAuth2Authenticator extends AbstractAuthenticator
{
private ResourceServer $resourceServer;
private EntityManagerInterface $entityManager;
public function __construct(ResourceServer $resourceServer, EntityManagerInterface $entityManager)
{
$this->resourceServer = $resourceServer;
$this->entityManager = $entityManager;
}
public function supports(Request $request): ?bool
{
return $request->headers->has('Authorization') &&
str_starts_with($request->headers->get('Authorization'), 'Bearer ');
}
public function authenticate(Request $request): Passport
{
$authorizationHeader = $request->headers->get('Authorization');
if (!$authorizationHeader) {
throw new CustomUserMessageAuthenticationException('No Authorization header provided');
}
if (!str_starts_with($authorizationHeader, 'Bearer ')) {
throw new CustomUserMessageAuthenticationException('Invalid Authorization header format');
}
$token = substr($authorizationHeader, 7); // Remove 'Bearer ' prefix
try {
// Create a PSR-7 request for the OAuth2 resource server
$psrRequest = new \Nyholm\Psr7\ServerRequest(
'GET',
$request->getUri(),
['Authorization' => 'Bearer ' . $token],
null,
'1.1'
);
// Validate the OAuth2 token using the resource server
$serverRequest = $this->resourceServer->validateAuthenticatedRequest($psrRequest);
// Extract user information from the token
$userId = $serverRequest->getAttribute('oauth_user_id');
$clientId = $serverRequest->getAttribute('oauth_client_id');
if (!$userId && !$clientId) {
throw new CustomUserMessageAuthenticationException('Invalid OAuth2 token: no user or client ID found');
}
return new SelfValidatingPassport(
new UserBadge($userId ?: $clientId, function ($identifier) use ($userId, $clientId) {
// Try to load user by ID first, then by client ID
$user = null;
if ($userId) {
$user = $this->entityManager->getRepository(UserList::class)->findOneBy(['username' => 'oauth_client']);
}
// If no user found by ID, try to find by client ID or create a system user
if (!$user && $clientId) {
// For client credentials flow, create a system user
$user = new UserList();
$user->setUsername('oauth_client');
$user->setIsActive(true);
$user->setIsSuperAdmin(false); // Set default value to prevent constraint violation
$user->setEMailAddressString('system@cesa.org.za');
$user->setFirstNameString('OAuth Client');
$user->setLastNameString($clientId);
$user->setEMailAddressSubscribed(false);
$user->setDeleted(false);
// Set a default password (not used for OAuth2)
$user->setPassword('oauth2_client_' . $clientId);
// Persist the user
$this->entityManager->persist($user);
$this->entityManager->flush();
}
if (!$user) {
throw new CustomUserMessageAuthenticationException('User not found');
}
return $user;
})
);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $e) {
throw new CustomUserMessageAuthenticationException('OAuth2 validation failed: ' . $e->getMessage());
} catch (\Exception $e) {
throw new CustomUserMessageAuthenticationException('Invalid OAuth2 token: ' . $e->getMessage());
}
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// Authentication successful, continue with the request
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
return new JsonResponse([
'error' => 'Unauthorized',
'message' => $exception->getMessage()
], Response::HTTP_UNAUTHORIZED);
}
}