From 01e60798524d7415292e6403139cffa9f5ba32ff Mon Sep 17 00:00:00 2001 From: Michiel Kodde Date: Thu, 12 Sep 2024 11:00:25 +0200 Subject: [PATCH] Check registration timeout occurences --- .../Component/RegistrationStatusComponent.ts | 6 ++++ assets/typescript/RegistrationStateMachine.ts | 8 +++++ src/Controller/RegistrationController.php | 7 +++- src/Tiqr/Legacy/TiqrService.php | 32 ++++++++++++++++++- src/Tiqr/TiqrServiceInterface.php | 2 ++ templates/default/registration.html.twig | 4 +++ translations/messages.en.yml | 1 + translations/messages.nl.yml | 1 + 8 files changed, 59 insertions(+), 2 deletions(-) diff --git a/assets/typescript/Component/RegistrationStatusComponent.ts b/assets/typescript/Component/RegistrationStatusComponent.ts index f23ec0ac..4d8a3a32 100644 --- a/assets/typescript/Component/RegistrationStatusComponent.ts +++ b/assets/typescript/Component/RegistrationStatusComponent.ts @@ -49,6 +49,12 @@ export class RegistrationStatusComponent { public showUnknownErrorHappened() { this.show('div.status.error'); } + /** + * Unknown error happened. Please try again by refreshing your browser. + */ + public showTimeoutHappened() { + this.show('div.status.timeout'); + } private hideAll() { jQuery('.status-container >').hide(); diff --git a/assets/typescript/RegistrationStateMachine.ts b/assets/typescript/RegistrationStateMachine.ts index 2f32cd69..9e170b59 100644 --- a/assets/typescript/RegistrationStateMachine.ts +++ b/assets/typescript/RegistrationStateMachine.ts @@ -12,6 +12,8 @@ export class RegistrationStateMachine { * Client-side only status. */ public static readonly ERROR = 'ERROR'; + public static readonly TIMEOUT = 'TIMEOUT'; + private previousStatus = RegistrationStateMachine.IDLE; constructor(private statusPollingService: StatusPollService, @@ -62,6 +64,12 @@ export class RegistrationStateMachine { this.qrCode.hide(); document.location.replace(this.finalizedUrl); break; + case RegistrationStateMachine.TIMEOUT: + this.qrCode.hide(); + this.statusUi.showTimeoutHappened(); + this.statusPollingService.stop(); + this.previousStatus = RegistrationStateMachine.ERROR; + break; default: this.unknownError(); return; diff --git a/src/Controller/RegistrationController.php b/src/Controller/RegistrationController.php index 80468c3c..5ba1c942 100644 --- a/src/Controller/RegistrationController.php +++ b/src/Controller/RegistrationController.php @@ -24,6 +24,7 @@ use Surfnet\GsspBundle\Service\RegistrationService; use Surfnet\GsspBundle\Service\StateHandlerInterface; use Surfnet\Tiqr\Exception\NoActiveAuthenrequestException; +use Surfnet\Tiqr\Tiqr\Legacy\TiqrService; use Surfnet\Tiqr\Tiqr\TiqrServiceInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -96,6 +97,7 @@ public function registration(Request $request): Response public function registrationStatus() : Response { $this->logger->info('Request for registration status'); + // Do we have a valid GSSP registration AuthnRequest in this session? if (!$this->registrationService->registrationRequired()) { $this->logger->error('There is no pending registration request'); @@ -103,7 +105,10 @@ public function registrationStatus() : Response return new Response('No registration required', Response::HTTP_BAD_REQUEST); } - // TODO: Check whether enrollment is expired here? + if ($this->tiqrService->isEnrollmentTimedOut()) { + $this->logger->info('The registration timed out'); + return new Response(TiqrService::ENROLLMENT_TIMEOUT_STATUS); + } $status = $this->tiqrService->getEnrollmentStatus(); $this->logger->info(sprintf('Send json response status "%s"', $status)); diff --git a/src/Tiqr/Legacy/TiqrService.php b/src/Tiqr/Legacy/TiqrService.php index a058f728..915d390a 100644 --- a/src/Tiqr/Legacy/TiqrService.php +++ b/src/Tiqr/Legacy/TiqrService.php @@ -24,6 +24,7 @@ use Psr\Log\LoggerInterface; use Surfnet\Tiqr\Exception\TiqrServerRuntimeException; use Surfnet\Tiqr\HealthCheck\HealthCheckResultDto; +use Surfnet\Tiqr\Service\TimeoutHelper; use Surfnet\Tiqr\Tiqr\Response\AuthenticationErrorResponse; use Surfnet\Tiqr\Tiqr\Response\AuthenticationResponse; use Surfnet\Tiqr\Tiqr\Response\RejectedAuthenticationResponse; @@ -47,6 +48,21 @@ final class TiqrService implements TiqrServiceInterface { public const ENROLL_KEYS_SESSION_NAME = 'enrollment-session-keys'; + + public const ENROLLMENT_TIMEOUT_STATUS = 'TIMEOUT'; + + /** + * Unix timestamp when the enrollment started + */ + private const ENROLLMENT_STARTED_AT = 'enrollment-started-at'; + + /** + * The time (in seconds) that is extracted from the timeout + * to prevent timeout issues right before the hard timeout + * time is reached. + */ + private const TIMEOUT_OFFSET = 296; + private SessionInterface $session; public function __construct( @@ -94,12 +110,14 @@ public function getEnrollmentSecret(string $enrollmentKey, string $sari): string public function generateEnrollmentKey(string $sari): string { $this->initSession(); - // We use a randomly generated user ID $this->logger->debug('Generating tiqr userId'); $userId = $this->generateId(); $this->logger->debug('Storing the userId=' . $userId . ' to session state'); $this->session->set('userId', $userId); + $registrationStartedAt = time(); + $this->logger->debug(sprintf('Storing the %s = %s', self::ENROLLMENT_STARTED_AT, $registrationStartedAt)); + $this->session->set(self::ENROLLMENT_STARTED_AT, $registrationStartedAt); // The session ID is used to link the tiqr library's enrollment session to the user's browser session $sessionId = $this->session->getId(); @@ -451,4 +469,16 @@ protected function getEnrollmentTimeout(): int { return Tiqr_Service::ENROLLMENT_EXPIRE; } + + public function isEnrollmentTimedOut(): bool + { + $this->initSession(); + $this->logger->debug('Checking if enrollment timeout is reached'); + return TimeoutHelper::isTimedOut( + time(), + $this->session->get(self::ENROLLMENT_STARTED_AT), + $this->getEnrollmentTimeout(), + self::TIMEOUT_OFFSET + ); + } } diff --git a/src/Tiqr/TiqrServiceInterface.php b/src/Tiqr/TiqrServiceInterface.php index 2ae689f6..181fd05d 100644 --- a/src/Tiqr/TiqrServiceInterface.php +++ b/src/Tiqr/TiqrServiceInterface.php @@ -242,4 +242,6 @@ public function sendNotification(string $notificationType, string $notificationA public function getSariForSessionIdentifier(string $identifier): string; public function stateStorageHealthCheck(): HealthCheckResultDto; + + public function isEnrollmentTimedOut(): bool; } diff --git a/templates/default/registration.html.twig b/templates/default/registration.html.twig index 021ad15a..4bef79de 100644 --- a/templates/default/registration.html.twig +++ b/templates/default/registration.html.twig @@ -45,6 +45,10 @@ {{ 'enrol.status.error' | trans }} {{ 'enrol.retry' | trans }}. +
+ {{ 'enrol.status.timeout' | trans }} + {{ 'enrol.retry' | trans }}. +
diff --git a/translations/messages.en.yml b/translations/messages.en.yml index 25d602b6..e53609f4 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -51,6 +51,7 @@ enrol: processed: One moment please... finalized: Your account is ready for use. error: Unknown error occurred. Please try again by refreshing your browser. + timeout: Registration timeout. Try again or refresh this page. download: Download tiqr for iOS/Android cancel: Cancel diff --git a/translations/messages.nl.yml b/translations/messages.nl.yml index e1d10312..28f62d57 100644 --- a/translations/messages.nl.yml +++ b/translations/messages.nl.yml @@ -55,6 +55,7 @@ enrol: processed: Een ogenblik geduld a.u.b. finalized: Je account is gereed voor gebruik. error: Er is een onbekende fout opgetreden. Probeer het opnieuw door uw browser te vernieuwen. + timeout: Registratie timeout. Ververs de pagina om het nogmaals te proberen. download: Download de tiqr-app voor iOS/Android cancel: Annuleren