Skip to content

Commit

Permalink
Merge branch 'main' of github.com:anzusystems/auth-bundle into update
Browse files Browse the repository at this point in the history
  • Loading branch information
marforon committed Aug 10, 2023
2 parents 4f6754b + 280bb99 commit e96e34b
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 18 deletions.
3 changes: 3 additions & 0 deletions src/Security/Authentication/ApiTokenAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public function supports(Request $request): bool
public function authenticate(Request $request): Passport
{
[$userId, $token] = $this->getCredentials($request);
if (empty($userId) || empty($token)) {
throw new AuthenticationException('Unauthorized request!');
}
$credentialsChecker = function (string $token, ApiTokenUserInterface $user): bool {
return $this->checkCredentials($token, $user);
};
Expand Down
15 changes: 8 additions & 7 deletions src/Util/HttpUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,9 @@ public function storeJwtOnResponse(Response $response, Token $token, DateTimeImm
$signature,
$lifetime
);
$refreshTokenExistenceCookie = $this->createCookie(
$this->cookieConfiguration->getRefreshTokenExistenceCookieName(),
'1',
$this->cookieConfiguration->getRefreshTokenLifetime(),
false
);

$response->headers->setCookie($payloadCookie);
$response->headers->setCookie($signatureCookie);
$response->headers->setCookie($refreshTokenExistenceCookie);
}

public function storeRefreshTokenOnResponse(Response $response, RefreshTokenDto $refreshTokenDto): void
Expand All @@ -130,7 +123,15 @@ public function storeRefreshTokenOnResponse(Response $response, RefreshTokenDto
$this->cookieConfiguration->getRefreshTokenLifetime(),
);

$refreshTokenExistenceCookie = $this->createCookie(
$this->cookieConfiguration->getRefreshTokenExistenceCookieName(),
'1',
$this->cookieConfiguration->getRefreshTokenLifetime(),
false
);

$response->headers->setCookie($refreshTokenCookie);
$response->headers->setCookie($refreshTokenExistenceCookie);
}

public function storeDeviceIdOnResponse(Response $response, string $deviceId): void
Expand Down
21 changes: 14 additions & 7 deletions src/Util/JwtUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,33 @@ public function __construct(
/**
* Can be used for creating a valid JWT. Useful especially for test environment.
*
* @param array<string, mixed> $claims
*
* @throws MissingConfigurationException
*/
public function create(string $authId, DateTimeImmutable $expiresAt = null): Plain
public function create(string $authId, DateTimeImmutable $expiresAt = null, array $claims = []): Plain
{
$privateCert = $this->jwtConfiguration->getPrivateCert();

if (empty($privateCert)) {
throw MissingConfigurationException::createForPrivateCertPath();
}

return (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates()))
$builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates()))
->permittedFor($this->jwtConfiguration->getAudience())
->issuedAt(new DateTimeImmutable())
->canOnlyBeUsedAfter(new DateTimeImmutable())
->expiresAt($expiresAt ?: new DateTimeImmutable(sprintf('+%d seconds', $this->jwtConfiguration->getLifetime())))
->relatedTo($authId)
->getToken(
$this->jwtConfiguration->getAlgorithm()->signer(),
InMemory::plainText($privateCert)
);
->relatedTo($authId);

foreach ($claims as $key => $value) {
$builder->withClaim($key, $value);
}

return $builder->getToken(
$this->jwtConfiguration->getAlgorithm()->signer(),
InMemory::plainText($privateCert)
);
}

public function validate(Token\Plain $token): bool
Expand Down
85 changes: 85 additions & 0 deletions tests/Util/HttpUtilTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace AnzuSystems\AuthBundle\Tests\Util;

use AnzuSystems\AuthBundle\Configuration\CookieConfiguration;
use AnzuSystems\AuthBundle\Configuration\JwtConfiguration;
use AnzuSystems\AuthBundle\Util\HttpUtil;
use AnzuSystems\AuthBundle\Util\JwtUtil;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;

final class HttpUtilTest extends TestCase
{
private HttpUtil $httpUtil;
private JwtConfiguration $jwtConfiguration;
private JwtUtil $jwtUtil;

protected function setUp(): void
{
$this->jwtConfiguration = new JwtConfiguration(
audience: 'anz',
algorithm: 'ES256',
publicCert: base64_decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFT0hIQzMvVDZ3cnVNTk40OTBqVE1maXNFa1BoTQp5eFNiQm1DK0hSYWF2Z1dLM25aNG1HNFlmVDRxMmF1L3V4TktBTjJvODJOdW84VTQ0ZXZkcExkQUhBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==', true),
privateCert: base64_decode('LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU9OdDBIdEdzUGdRRytKY2VGUk5GdlRZMVVVeDVITTdqQzNVS1ZHRHBlS0tvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFT0hIQzMvVDZ3cnVNTk40OTBqVE1maXNFa1BoTXl4U2JCbUMrSFJhYXZnV0szblo0bUc0WQpmVDRxMmF1L3V4TktBTjJvODJOdW84VTQ0ZXZkcExkQUhBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=', true),
lifetime: 3_600
);
$this->jwtUtil = new JwtUtil($this->jwtConfiguration);

$cookieConfiguration = new CookieConfiguration(
domain: '.example.com',
secure: true,
jwtPayloadCookieName: 'qux',
jwtSignatureCookieName: 'quux',
deviceIdCookieName: 'corge',
refreshTokenCookieName: 'garply',
refreshTokenExistenceCookieName: 'grault',
refreshTokenLifetime: 3600,
);

$this->httpUtil = new HttpUtil(
cookieConfiguration: $cookieConfiguration,
jwtConfiguration: $this->jwtConfiguration,
authRedirectDefaultUrl: 'https://example.com',
authRedirectQueryUrlAllowedPattern: '^https?://(.*)\.example\.com$'
);
}

public function testStoreJwtOnResponse(): void
{
$response = new Response();
$token = $this->jwtUtil->create('123');
$expireAt = new DateTimeImmutable(sprintf('+%d seconds', $this->jwtConfiguration->getLifetime()));
$this->httpUtil->storeJwtOnResponse($response, $token, $expireAt);

$cookies = $response->headers->getCookies();
$this->assertCount(2, $cookies);

/** @var Cookie $payloadCookie */
$payloadCookie = current(
array_filter($cookies, static function (Cookie $cookie) {
return $cookie->getName() === 'qux';
})
);

/** @var Cookie $signCookie */
$signCookie = current(
array_filter($cookies, static function (Cookie $cookie) {
return $cookie->getName() === 'quux';
})
);

$this->assertSame($token->toString(), $payloadCookie->getValue() . '.' . $signCookie->getValue());
$this->assertTrue($payloadCookie->isSecure());
$this->assertFalse($payloadCookie->isHttpOnly());
$this->assertSame('.example.com', $payloadCookie->getDomain());

$this->assertTrue($signCookie->isSecure());
$this->assertTrue($signCookie->isHttpOnly());
$this->assertSame('.example.com', $signCookie->getDomain());
}
}
22 changes: 20 additions & 2 deletions tests/Util/JwtUtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ protected function setUp(): void
$this->jwtConfiguration = new JwtConfiguration(
audience: 'anz',
algorithm: 'ES256',
publicCert: base64_decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFT0hIQzMvVDZ3cnVNTk40OTBqVE1maXNFa1BoTQp5eFNiQm1DK0hSYWF2Z1dLM25aNG1HNFlmVDRxMmF1L3V4TktBTjJvODJOdW84VTQ0ZXZkcExkQUhBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=='),
privateCert: base64_decode('LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU9OdDBIdEdzUGdRRytKY2VGUk5GdlRZMVVVeDVITTdqQzNVS1ZHRHBlS0tvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFT0hIQzMvVDZ3cnVNTk40OTBqVE1maXNFa1BoTXl4U2JCbUMrSFJhYXZnV0szblo0bUc0WQpmVDRxMmF1L3V4TktBTjJvODJOdW84VTQ0ZXZkcExkQUhBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo='),
publicCert: base64_decode('LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFT0hIQzMvVDZ3cnVNTk40OTBqVE1maXNFa1BoTQp5eFNiQm1DK0hSYWF2Z1dLM25aNG1HNFlmVDRxMmF1L3V4TktBTjJvODJOdW84VTQ0ZXZkcExkQUhBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==', true),
privateCert: base64_decode('LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU9OdDBIdEdzUGdRRytKY2VGUk5GdlRZMVVVeDVITTdqQzNVS1ZHRHBlS0tvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFT0hIQzMvVDZ3cnVNTk40OTBqVE1maXNFa1BoTXl4U2JCbUMrSFJhYXZnV0szblo0bUc0WQpmVDRxMmF1L3V4TktBTjJvODJOdW84VTQ0ZXZkcExkQUhBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=', true),
lifetime: 3_600
);
$this->jwtUtil = new JwtUtil($this->jwtConfiguration);
Expand All @@ -44,4 +44,22 @@ public function testCreate(): void
);
$this->assertSame(['anz'], $jwt->claims()->get(RegisteredClaims::AUDIENCE));
}

/**
* @throws MissingConfigurationException
*/
public function testCreateWithClaims(): void
{
$expireAt = new DateTimeImmutable(sprintf('+%d seconds', $this->jwtConfiguration->getLifetime()));
$jwt = $this->jwtUtil->create('123', $expireAt, ['foo' => 'bar', 'qux' => 'quux']);
$this->assertInstanceOf(Plain::class, $jwt);
$this->assertSame('123', $jwt->claims()->get(RegisteredClaims::SUBJECT));
$this->assertSame('bar', $jwt->claims()->get('foo'));
$this->assertSame('quux', $jwt->claims()->get('qux'));
$this->assertSame(
$expireAt->getTimestamp(),
$jwt->claims()->get(RegisteredClaims::EXPIRATION_TIME)->getTimestamp()
);
$this->assertSame(['anz'], $jwt->claims()->get(RegisteredClaims::AUDIENCE));
}
}
3 changes: 1 addition & 2 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

declare(strict_types=1);

require dirname(__DIR__).'/vendor/autoload.php';

require dirname(__DIR__) . '/vendor/autoload.php';

0 comments on commit e96e34b

Please sign in to comment.