Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 23 additions & 27 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,27 @@
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
convertDeprecationsToExceptions="false"
>
<testsuites>
<testsuite name="Auth Bundle">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<ini name="display_errors" value="1" />
<ini name="error_reporting" value="-1" />
<env name="APP_ENV" value="test" force="true" />
<env name="SHELL_VERBOSITY" value="-1" />
<env name="SYMFONY_PHPUNIT_REMOVE" value="" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" force="true" />
</php>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">.</directory>
</include>
<exclude>
<directory>./vendor</directory>
<directory>./tests</directory>
</exclude>
</coverage>
<listeners>
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"/>
</listeners>
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Auth Bundle">
<directory>./tests</directory>
</testsuite>
</testsuites>
<php>
<ini name="display_errors" value="1"/>
<ini name="error_reporting" value="-1"/>
<env name="APP_ENV" value="test" force="true"/>
<env name="SHELL_VERBOSITY" value="-1"/>
<env name="SYMFONY_PHPUNIT_REMOVE" value=""/>
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" force="true"/>
</php>
<source>
<include>
<directory suffix=".php">.</directory>
</include>
<exclude>
<directory>./vendor</directory>
<directory>./tests</directory>
</exclude>
</source>
</phpunit>
14 changes: 14 additions & 0 deletions src/Configuration/CookieConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/AnzuSystemsAuthExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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')
Expand Down
2 changes: 1 addition & 1 deletion src/Domain/Process/GrantAccessOnResponseProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 9 additions & 3 deletions src/Domain/Process/OAuth2/GrantAccessByOAuth2TokenProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -44,6 +46,7 @@ public function __construct(
private readonly LoggerInterface $appLogger,
private readonly LogContextFactory $contextFactory,
private readonly string $authMethod,
private readonly EventDispatcherInterface $eventDispatcher,
) {
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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 .= '?';
Expand All @@ -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());
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Domain/Process/RefreshTokenProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
50 changes: 50 additions & 0 deletions src/Event/AuthTargetUrlEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace AnzuSystems\AuthBundle\Event;

use AnzuSystems\AuthBundle\Contracts\AnzuAuthUserInterface;
use AnzuSystems\AuthBundle\Model\Enum\UserOAuthLoginState;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Contracts\EventDispatcher\Event;

final class AuthTargetUrlEvent extends Event
{
public const string NAME = 'anzu_auth.target_url';

public function __construct(
private string $targetUrl,
private readonly Request $request,
private readonly UserOAuthLoginState $loginState,
private readonly ?AnzuAuthUserInterface $authUser = null
) {
}

public function getTargetUrl(): string
{
return $this->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;
}
}
1 change: 1 addition & 0 deletions src/Resources/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'))
Expand Down
4 changes: 2 additions & 2 deletions src/Util/HttpUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -190,7 +190,7 @@ private function createCookie(
$this->cookieConfiguration->isSecure(),
$httpOnly,
false,
Cookie::SAMESITE_STRICT
$this->cookieConfiguration->getSameSite(),
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Util/JwtUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 2 additions & 0 deletions tests/DependencyInjection/AnzuSystemsAuthExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -129,6 +130,7 @@ private function getFullConfig(): array
$yaml = <<<EOF
cookie:
domain: .example.com
same_site: lax
secure: true
jwt:
public_cert: 'foo_public_cert'
Expand Down
3 changes: 3 additions & 0 deletions tests/Util/HttpUtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ protected function setUp(): void

$cookieConfiguration = new CookieConfiguration(
domain: '.example.com',
sameSite: 'strict',
secure: true,
jwtPayloadCookieName: 'qux',
jwtSignatureCookieName: 'quux',
Expand Down Expand Up @@ -76,10 +77,12 @@ public function testStoreJwtOnResponse(): void
$this->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());
}
}