uriSigner = new UriSigner($secret); } public function sign(AccommodationBooking $booking): string { $issuedAt = $booking->getAccessLinkIssuedAt(); if (null === $issuedAt) { throw new \LogicException('Cannot sign an access link before accessLinkIssuedAt is set.'); } $url = $this->urlGenerator->generate( 'app_groups_offer', ['uuid' => $booking->getUuid(), self::TIMESTAMP_PARAM => $issuedAt->getTimestamp()], UrlGeneratorInterface::ABSOLUTE_URL, ); return $this->uriSigner->sign($url); } public function expiresAt(AccommodationBooking $booking): ?\DateTimeImmutable { return $booking->getAccessLinkIssuedAt()?->modify(sprintf('+%d days', self::LINK_TTL_DAYS)); } public function isValidLinkRequest(Request $request, AccommodationBooking $booking): bool { $issuedAt = $booking->getAccessLinkIssuedAt(); if (null === $issuedAt) { return false; } if (!$this->uriSigner->checkRequest($request)) { return false; } $timestampParam = $request->query->get(self::TIMESTAMP_PARAM); if (null === $timestampParam || (int) $timestampParam !== $issuedAt->getTimestamp()) { return false; } $expiresAt = $this->expiresAt($booking); return null !== $expiresAt && new \DateTimeImmutable() <= $expiresAt; } /** * Marks the current session as authorized to view/act on this booking's offer. * Called once, after a successful {@see isValidLinkRequest()} check at the * signed-link entry point — every other route in this flow then trusts the * session instead of re-deriving cryptographic validity from its own URL * (which wouldn't work anyway, since the signature is bound to the exact * URI it was generated for). */ public function authorizeSession(Request $request, AccommodationBooking $booking): void { $request->getSession()->set($this->sessionKey($booking), $booking->getAccessLinkIssuedAt()?->getTimestamp()); } public function isSessionAuthorized(Request $request, AccommodationBooking $booking): bool { $issuedAt = $booking->getAccessLinkIssuedAt(); if (null === $issuedAt) { return false; } if ($request->getSession()->get($this->sessionKey($booking)) !== $issuedAt->getTimestamp()) { return false; } $expiresAt = $this->expiresAt($booking); return null !== $expiresAt && new \DateTimeImmutable() <= $expiresAt; } private function sessionKey(AccommodationBooking $booking): string { return 'accommodation_offer_access_'.$booking->getUuid(); } }