Skip to content

Commit

Permalink
Fixed use case to refresh token if access token is null.
Browse files Browse the repository at this point in the history
Added AJAX request handling and improved log messages.

close #30
  • Loading branch information
mainick committed Nov 24, 2024
1 parent 3f29462 commit 9a89f07
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 8 deletions.
28 changes: 27 additions & 1 deletion src/Security/EntryPoint/KeycloakAuthenticationEntryPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Mainick\KeycloakClientBundle\Security\EntryPoint;

use Mainick\KeycloakClientBundle\DTO\KeycloakAuthorizationCodeEnum;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
Expand All @@ -15,16 +17,40 @@
final readonly class KeycloakAuthenticationEntryPoint implements AuthenticationEntryPointInterface
{
public function __construct(
private UrlGeneratorInterface $urlGenerator
private UrlGeneratorInterface $urlGenerator,
private ?LoggerInterface $keycloakClientLogger = null,
) {
}

public function start(Request $request, ?AuthenticationException $authException = null): Response
{
// Handling AJAX requests
if ($request->isXmlHttpRequest()) {
return new JsonResponse(
[
'code' => Response::HTTP_UNAUTHORIZED,
'message' => 'Authentication Required',
'login_url' => $this->urlGenerator->generate('mainick_keycloak_security_auth_connect'),
],
Response::HTTP_UNAUTHORIZED
);
}

if ($request->hasSession()) {
$request->getSession()->set(KeycloakAuthorizationCodeEnum::LOGIN_REFERRER, $request->getUri());

$request->getSession()->getBag('flashes')->add(
'info',
'Please log in to access this page',
);
}

$this->keycloakClientLogger?->info('KeycloakAuthenticationEntryPoint::start', [
'path' => $request->getPathInfo(),
'error' => $authException?->getMessage(),
'loginReferrer' => $request->getUri(),
]);

return new RedirectResponse(
$this->urlGenerator->generate('mainick_keycloak_security_auth_connect'),
Response::HTTP_TEMPORARY_REDIRECT
Expand Down
43 changes: 36 additions & 7 deletions src/Security/User/KeycloakUserProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Mainick\KeycloakClientBundle\Interface\IamClientInterface;
use Mainick\KeycloakClientBundle\Token\KeycloakResourceOwner;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
Expand All @@ -28,11 +29,33 @@ public function refreshUser(UserInterface $user): UserInterface
}

$accessToken = $user->getAccessToken();
if ($accessToken && $accessToken->hasExpired()) {
$accessToken = $this->iamClient->refreshToken($accessToken);
if (!$accessToken) {
$this->keycloakClientLogger->error('KeycloakUserProvider::refreshUser', [
'message' => 'User does not have an access token.',
'user_id' => $user->getUserIdentifier(),
]);
throw new AuthenticationException('No valid access token available. Please login again.');
}

try {
if ($accessToken->hasExpired()) {
$accessToken = $this->iamClient->refreshToken($accessToken);
if (!$accessToken) {
throw new AuthenticationException('Failed to refresh user session. Please login again.');
}
}

return $this->loadUserByIdentifier($accessToken);
}
catch (\Exception $e) {
$this->keycloakClientLogger->error('KeycloakUserProvider::refreshUser', [
'error' => $e->getMessage(),
'message' => 'Failed to refresh user access token',
'user_id' => $user->getUserIdentifier(),
]);

return $this->loadUserByIdentifier($accessToken);
throw new AuthenticationException('Failed to refresh user session. Please login again.');
}
}

public function supportsClass(string $class): bool
Expand All @@ -49,21 +72,27 @@ public function loadUserByIdentifier($identifier): UserInterface
try {
$resourceOwner = $this->iamClient->fetchUserFromToken($identifier);
if (!$resourceOwner) {
throw new UserNotFoundException(sprintf('User with access token "%s" not found.', $identifier));
$this->keycloakClientLogger->info('KeycloakUserProvider::loadUserByIdentifier', [
'message' => 'User not found',
'token' => $identifier->getToken(),
]);
throw new UserNotFoundException('User not found or invalid token.');
}

$this->keycloakClientLogger->info('KeycloakUserProvider::loadUserByIdentifier', [
'resourceOwner' => $resourceOwner->toArray(),
]);

return $resourceOwner;
}
catch (\UnexpectedValueException $e) {
$this->keycloakClientLogger->warning('KeycloakUserProvider::loadUserByIdentifier', [
'error' => $e->getMessage(),
'message' => 'User should have been disconnected from Keycloak server',
'token' => $identifier->getToken(),
]);

throw new UserNotFoundException(sprintf('User with access token "%s" not found.', $identifier));
throw new UserNotFoundException('Failed to load user from token.');
}

return $resourceOwner;
}
}

0 comments on commit 9a89f07

Please sign in to comment.