diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 808c625..8b5938f 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -5,31 +5,27 @@
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
- convertDeprecationsToExceptions="false"
->
-
-
- ./tests
-
-
-
-
-
-
-
-
-
-
-
-
- .
-
-
- ./vendor
- ./tests
-
-
-
-
-
+ cacheDirectory=".phpunit.cache">
+
+
+ ./tests
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+ ./vendor
+ ./tests
+
+
diff --git a/src/Configuration/CookieConfiguration.php b/src/Configuration/CookieConfiguration.php
index 4fc906a..fd27b08 100644
--- a/src/Configuration/CookieConfiguration.php
+++ b/src/Configuration/CookieConfiguration.php
@@ -4,11 +4,17 @@
namespace AnzuSystems\AuthBundle\Configuration;
+use Symfony\Component\HttpFoundation\Cookie;
+
final class CookieConfiguration
{
public function __construct(
private readonly ?string $domain,
private readonly bool $secure,
+ /**
+ * @var Cookie::SAMESITE_*|''|null
+ */
+ private readonly ?string $sameSite,
private readonly string $jwtPayloadCookieName,
private readonly string $jwtSignatureCookieName,
private readonly string $deviceIdCookieName,
@@ -23,6 +29,14 @@ public function getDomain(): ?string
return $this->domain;
}
+ /**
+ * @return Cookie::SAMESITE_*|''|null
+ */
+ public function getSameSite(): ?string
+ {
+ return $this->sameSite;
+ }
+
public function isSecure(): bool
{
return $this->secure;
diff --git a/src/DependencyInjection/AnzuSystemsAuthExtension.php b/src/DependencyInjection/AnzuSystemsAuthExtension.php
index 5bd8bca..cb3dde9 100644
--- a/src/DependencyInjection/AnzuSystemsAuthExtension.php
+++ b/src/DependencyInjection/AnzuSystemsAuthExtension.php
@@ -38,6 +38,7 @@ public function load(array $configs, ContainerBuilder $container): void
$cookieSection = $processedConfig['cookie'];
$container->setParameter('anzu_systems.auth_bundle.cookie.domain', $cookieSection['domain']);
+ $container->setParameter('anzu_systems.auth_bundle.cookie.same_site', $cookieSection['same_site']);
$container->setParameter('anzu_systems.auth_bundle.cookie.secure', $cookieSection['secure']);
$container->setParameter('anzu_systems.auth_bundle.cookie.device_id_name', $cookieSection['device_id_name']);
$container->setParameter('anzu_systems.auth_bundle.cookie.jwt.payload_part_name', $cookieSection['jwt']['payload_part_name']);
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 509d535..f231240 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -13,6 +13,7 @@
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
+use Symfony\Component\HttpFoundation\Cookie;
final class Configuration implements ConfigurationInterface
{
@@ -41,6 +42,10 @@ private function addCookieSection(): NodeDefinition
->addDefaultsIfNotSet()
->children()
->scalarNode('domain')->defaultValue(null)->end()
+ ->scalarNode('same_site')
+ ->defaultValue(Cookie::SAMESITE_STRICT)
+ ->info('SameSite attribute for cookies (lax, strict, none or null)')
+ ->end()
->booleanNode('secure')->isRequired()->end()
->scalarNode('device_id_name')->defaultValue('anz_di')->end()
->arrayNode('jwt')
diff --git a/src/Domain/Process/GrantAccessOnResponseProcess.php b/src/Domain/Process/GrantAccessOnResponseProcess.php
index d408d0f..40283b0 100644
--- a/src/Domain/Process/GrantAccessOnResponseProcess.php
+++ b/src/Domain/Process/GrantAccessOnResponseProcess.php
@@ -32,7 +32,7 @@ public function __construct(
/**
* @throws Exception
*/
- public function execute(string $userId, Request $request, Response $response = null): Response
+ public function execute(string $userId, Request $request, ?Response $response = null): Response
{
$jwtExpiresAt = new DateTimeImmutable(sprintf('+%d seconds', $this->jwtConfiguration->getLifetime()));
$jwt = $this->jwtUtil->create($userId, $jwtExpiresAt);
diff --git a/src/Domain/Process/OAuth2/GrantAccessByOAuth2TokenProcess.php b/src/Domain/Process/OAuth2/GrantAccessByOAuth2TokenProcess.php
index 9349285..43ce5d1 100644
--- a/src/Domain/Process/OAuth2/GrantAccessByOAuth2TokenProcess.php
+++ b/src/Domain/Process/OAuth2/GrantAccessByOAuth2TokenProcess.php
@@ -7,6 +7,7 @@
use AnzuSystems\AuthBundle\Contracts\AnzuAuthUserInterface;
use AnzuSystems\AuthBundle\Contracts\OAuth2AuthUserRepositoryInterface;
use AnzuSystems\AuthBundle\Domain\Process\GrantAccessOnResponseProcess;
+use AnzuSystems\AuthBundle\Event\AuthTargetUrlEvent;
use AnzuSystems\AuthBundle\Exception\InvalidJwtException;
use AnzuSystems\AuthBundle\Exception\UnsuccessfulAccessTokenRequestException;
use AnzuSystems\AuthBundle\Exception\UnsuccessfulUserInfoRequestException;
@@ -21,6 +22,7 @@
use Exception;
use Lcobucci\JWT\Token\RegisteredClaims;
use Psr\Log\LoggerInterface;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -44,6 +46,7 @@ public function __construct(
private readonly LoggerInterface $appLogger,
private readonly LogContextFactory $contextFactory,
private readonly string $authMethod,
+ private readonly EventDispatcherInterface $eventDispatcher,
) {
}
@@ -91,7 +94,7 @@ public function execute(Request $request): Response
}
try {
- $response = $this->createRedirectResponseForRequest($request, UserOAuthLoginState::Success);
+ $response = $this->createRedirectResponseForRequest($request, UserOAuthLoginState::Success, $authUser);
return $this->grantAccessOnResponseProcess->execute($authUser->getAuthId(), $request, $response);
} catch (Exception $exception) {
@@ -101,7 +104,7 @@ public function execute(Request $request): Response
}
}
- public function createRedirectResponseForRequest(Request $request, UserOAuthLoginState $loginState): RedirectResponse
+ public function createRedirectResponseForRequest(Request $request, UserOAuthLoginState $loginState, ?AnzuAuthUserInterface $authUser = null): RedirectResponse
{
$redirectUrl = $this->httpUtil->getAuthRedirectUrlFromRequest($request);
$redirectUrl .= '?';
@@ -110,7 +113,10 @@ public function createRedirectResponseForRequest(Request $request, UserOAuthLogi
self::TIMESTAMP_QUERY_PARAM => time(),
]);
- return new RedirectResponse($redirectUrl);
+ $event = new AuthTargetUrlEvent($redirectUrl, $request, $loginState, $authUser);
+ $this->eventDispatcher->dispatch($event, AuthTargetUrlEvent::NAME);
+
+ return new RedirectResponse($event->getTargetUrl());
}
/**
diff --git a/src/Domain/Process/RefreshTokenProcess.php b/src/Domain/Process/RefreshTokenProcess.php
index e19e69b..fb00c89 100644
--- a/src/Domain/Process/RefreshTokenProcess.php
+++ b/src/Domain/Process/RefreshTokenProcess.php
@@ -24,7 +24,7 @@ public function __construct(
/**
* @throws Exception
*/
- public function execute(Request $request, Response $response = null): Response
+ public function execute(Request $request, ?Response $response = null): Response
{
$response ??= new JsonResponse();
try {
diff --git a/src/Event/AuthTargetUrlEvent.php b/src/Event/AuthTargetUrlEvent.php
new file mode 100644
index 0000000..ea08693
--- /dev/null
+++ b/src/Event/AuthTargetUrlEvent.php
@@ -0,0 +1,50 @@
+targetUrl;
+ }
+
+ public function setTargetUrl(string $targetUrl): self
+ {
+ $this->targetUrl = $targetUrl;
+
+ return $this;
+ }
+
+ public function getRequest(): Request
+ {
+ return $this->request;
+ }
+
+ public function getLoginState(): UserOAuthLoginState
+ {
+ return $this->loginState;
+ }
+
+ public function getAuthUser(): ?AnzuAuthUserInterface
+ {
+ return $this->authUser;
+ }
+}
diff --git a/src/Resources/config/services.php b/src/Resources/config/services.php
index 9a68137..46d935d 100644
--- a/src/Resources/config/services.php
+++ b/src/Resources/config/services.php
@@ -18,6 +18,7 @@
$services
->set(CookieConfiguration::class)
->arg('$domain', param('anzu_systems.auth_bundle.cookie.domain'))
+ ->arg('$sameSite', param('anzu_systems.auth_bundle.cookie.same_site'))
->arg('$secure', param('anzu_systems.auth_bundle.cookie.secure'))
->arg('$deviceIdCookieName', param('anzu_systems.auth_bundle.cookie.device_id_name'))
->arg('$jwtPayloadCookieName', param('anzu_systems.auth_bundle.cookie.jwt.payload_part_name'))
diff --git a/src/Util/HttpUtil.php b/src/Util/HttpUtil.php
index 112ae9a..61ee79b 100644
--- a/src/Util/HttpUtil.php
+++ b/src/Util/HttpUtil.php
@@ -91,7 +91,7 @@ public function grabDeviceIdFromRequest(Request $request): string
/**
* @throws InvalidJwtException
*/
- public function storeJwtOnResponse(Response $response, Token $token, DateTimeImmutable $expiresAt = null): void
+ public function storeJwtOnResponse(Response $response, Token $token, ?DateTimeImmutable $expiresAt = null): void
{
$rawToken = $token->toString();
/** @psalm-suppress PossiblyUndefinedArrayOffset */
@@ -190,7 +190,7 @@ private function createCookie(
$this->cookieConfiguration->isSecure(),
$httpOnly,
false,
- Cookie::SAMESITE_STRICT
+ $this->cookieConfiguration->getSameSite(),
);
}
diff --git a/src/Util/JwtUtil.php b/src/Util/JwtUtil.php
index efc5cff..d494c6c 100644
--- a/src/Util/JwtUtil.php
+++ b/src/Util/JwtUtil.php
@@ -35,7 +35,7 @@ public function __construct(
*
* @throws MissingConfigurationException
*/
- public function create(string $authId, DateTimeImmutable $expiresAt = null, array $claims = []): Plain
+ public function create(string $authId, ?DateTimeImmutable $expiresAt = null, array $claims = []): Plain
{
$privateCert = $this->jwtConfiguration->getPrivateCert();
diff --git a/tests/DependencyInjection/AnzuSystemsAuthExtensionTest.php b/tests/DependencyInjection/AnzuSystemsAuthExtensionTest.php
index 9bab4c7..8bf7fd4 100644
--- a/tests/DependencyInjection/AnzuSystemsAuthExtensionTest.php
+++ b/tests/DependencyInjection/AnzuSystemsAuthExtensionTest.php
@@ -66,6 +66,7 @@ public function testFullConfiguration(): void
$loader->load([$config], $this->configuration);
$this->assertParameter('.example.com', 'anzu_systems.auth_bundle.cookie.domain');
+ $this->assertParameter('lax', 'anzu_systems.auth_bundle.cookie.same_site');
$this->assertParameter(true, 'anzu_systems.auth_bundle.cookie.secure');
$this->assertParameter('anz_di', 'anzu_systems.auth_bundle.cookie.device_id_name');
$this->assertParameter('anz_jp', 'anzu_systems.auth_bundle.cookie.jwt.payload_part_name');
@@ -129,6 +130,7 @@ private function getFullConfig(): array
$yaml = <<assertSame($token->toString(), $payloadCookie->getValue() . '.' . $signCookie->getValue());
$this->assertTrue($payloadCookie->isSecure());
$this->assertFalse($payloadCookie->isHttpOnly());
+ $this->assertSame('strict', $payloadCookie->getSameSite());
$this->assertSame('.example.com', $payloadCookie->getDomain());
$this->assertTrue($signCookie->isSecure());
$this->assertTrue($signCookie->isHttpOnly());
+ $this->assertSame('strict', $payloadCookie->getSameSite());
$this->assertSame('.example.com', $signCookie->getDomain());
}
}