diff --git a/Classes/Authentication/OpenIdConnectProvider.php b/Classes/Authentication/OpenIdConnectProvider.php index c1a6c43..bcae2a6 100644 --- a/Classes/Authentication/OpenIdConnectProvider.php +++ b/Classes/Authentication/OpenIdConnectProvider.php @@ -86,12 +86,11 @@ public function authenticate(TokenInterface $authenticationToken): void if (!isset($this->options['accountIdentifierTokenValueName'])) { $this->options['accountIdentifierTokenValueName'] = 'sub'; } - if (!isset($this->options['jwtCookieName'])) { - $this->options['jwtCookieName'] = 'flownative_oidc_jwt'; - } + $serviceName = $this->options['serviceName']; + $jwtCookieName = $serviceName . '-jwt'; try { - $jwks = (new OpenIdConnectClient($this->options['serviceName']))->getJwks(); - $identityToken = $authenticationToken->extractIdentityTokenFromRequest($this->options['jwtCookieName']); + $jwks = (new OpenIdConnectClient($serviceName))->getJwks(); + $identityToken = $authenticationToken->extractIdentityTokenFromRequest($jwtCookieName); if (!$identityToken->hasValidSignature($jwks)) { throw new SecurityException(sprintf('Open ID Connect: The identity token provided by the OIDC provider had an invalid signature'), 1561479176); } diff --git a/Classes/Http/SetJwtCookieComponent.php b/Classes/Http/SetJwtCookieComponent.php index 3616802..e0b6057 100644 --- a/Classes/Http/SetJwtCookieComponent.php +++ b/Classes/Http/SetJwtCookieComponent.php @@ -10,6 +10,7 @@ use Neos\Flow\Http\Cookie; use Neos\Flow\Log\Utility\LogEnvironment; use Neos\Flow\Security\Context as SecurityContext; +use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; final class SetJwtCookieComponent implements ComponentInterface @@ -59,6 +60,10 @@ public function initializeObject() */ public function handle(ComponentContext $componentContext): void { + if ($this->isLogoutRequest($componentContext)) { + $this->handleLogout($componentContext); + return; + } if (!$this->securityContext->isInitialized() && !$this->securityContext->canBeInitialized()) { $this->logger->debug('OpenID Connect: Cannot send JWT cookie because the security context could not be initialized.', LogEnvironment::fromMethodName(__METHOD__)); return; @@ -70,6 +75,7 @@ public function handle(ComponentContext $componentContext): void if ($account === null) { $this->logger->debug(sprintf('OpenID Connect: No account is authenticated using the provider %s, removing JWT cookie "%s" if it exists.', $this->options['authenticationProviderName'], $this->options['cookie']['name']), LogEnvironment::fromMethodName(__METHOD__)); $this->removeJwtCookie($componentContext); + $this->logger->info(sprintf('OpenID Connect Client: (%s) Logout requested (via query parameter) removing JWT cookie for service "%s".', get_class($this), $this->options['serviceName'])); return; } @@ -95,6 +101,31 @@ private function isOpenIdConnectAuthentication(): bool return false; } + /** + * @param ComponentContext $componentContext + * @return bool + */ + private function isLogoutRequest(ComponentContext $componentContext): bool + { + $httpRequest = $componentContext->getHttpRequest(); + if (!$httpRequest instanceof ServerRequestInterface) { + return false; + } + $queryParams = $httpRequest->getQueryParams(); + return isset($queryParams['logout']) && $queryParams['logout'] === $this->options['serviceName']; + } + + /** + * @param ComponentContext $componentContext + * @return void + */ + private function handleLogout(ComponentContext $componentContext): void + { + $this->removeJwtCookie($componentContext); + $this->logger->info(sprintf('OpenID Connect Client: (%s) Logout requested (via query parameter) removing JWT cookie for service "%s".', get_class($this), $this->options['serviceName'])); + $componentContext->replaceHttpResponse($componentContext->getHttpResponse()->withHeader('Location', (string)$componentContext->getHttpRequest()->getUri()->withQuery(''))); + } + /** * @param ComponentContext $componentContext * @param string $jwt @@ -113,4 +144,9 @@ private function removeJwtCookie(ComponentContext $componentContext): void $emptyJwtCookie = new Cookie($this->options['cookie']['name'], '', 1, null, null, '/', $this->options['cookie']['secure'], false, $this->options['cookie']['sameSite']); $componentContext->replaceHttpResponse($componentContext->getHttpResponse()->withAddedHeader('Set-Cookie', (string)$emptyJwtCookie)); } + + private function jwtCookieName(): string + { + return $this->options['serviceName'] . '-jwt'; + } } diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 8d5fd7e..50cf9a0 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -16,7 +16,8 @@ Flownative: # chain: # 'Flownative.OpenIdConnect.Client:setJwtCookie': # componentOptions: -# cookieName: 'your-own-cookie-name-jwt' +# # Use the same service name as below - the corresponding JWT will be stored in a cookie named "-jwt" +# serviceName: 'exampleService' # security: # authentication: @@ -27,7 +28,6 @@ Flownative: # providerOptions: # roles: ['Acme.MyPackage:User'] # accountIdentifierTokenValueName: 'inum' -# jwtCookieName: 'your-own-cookie-name-jwt' # serviceName: 'exampleService' # token: 'Flownative\OpenIdConnect\Client\Authentication\OpenIdConnectToken' # requestPatterns: