Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty ttl for testing purpose #330

Merged
merged 1 commit into from
May 2, 2017
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
16 changes: 11 additions & 5 deletions Services/JWSProvider/DefaultJWSProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* JWS Provider, Namshi\JOSE library integration.
* Supports OpenSSL and phpseclib crypto engines.
*
* @internal
* @final
*
* @author Robin Chalas <[email protected]>
*/
Expand Down Expand Up @@ -47,7 +47,7 @@ class DefaultJWSProvider implements JWSProviderInterface
*/
public function __construct(KeyLoaderInterface $keyLoader, $cryptoEngine, $signatureAlgorithm, $ttl)
{
if (!is_numeric($ttl)) {
if (null !== $ttl && !is_numeric($ttl)) {
throw new \InvalidArgumentException(sprintf('The TTL should be a numeric value, got %s instead.', $ttl));
}

Expand All @@ -70,9 +70,14 @@ public function __construct(KeyLoaderInterface $keyLoader, $cryptoEngine, $signa
*/
public function create(array $payload)
{
$jws = new JWS(['alg' => $this->signatureAlgorithm], $this->cryptoEngine);
$jws = new JWS(['alg' => $this->signatureAlgorithm], $this->cryptoEngine);
$claims = ['iat' => time()];

$jws->setPayload($payload + ['exp' => (time() + $this->ttl), 'iat' => time()]);
if (null !== $this->ttl) {
$claims['exp'] = time() + $this->ttl;
}

$jws->setPayload($payload + $claims);
$jws->sign(
$this->keyLoader->loadKey('private'),
$this->keyLoader->getPassphrase()
Expand All @@ -90,7 +95,8 @@ public function load($token)

return new LoadedJWS(
$jws->getPayload(),
$jws->verify($this->keyLoader->loadKey('public'), $this->signatureAlgorithm)
$jws->verify($this->keyLoader->loadKey('public'), $this->signatureAlgorithm),
null !== $this->ttl
);
}

Expand Down
31 changes: 20 additions & 11 deletions Services/JWSProvider/LcobucciJWSProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
use Lexik\Bundle\JWTAuthenticationBundle\Signature\CreatedJWS;
use Lexik\Bundle\JWTAuthenticationBundle\Signature\LoadedJWS;

/**
* @final
*
* @author Robin Chalas <[email protected]>
*/
class LcobucciJWSProvider implements JWSProviderInterface
{
/**
Expand All @@ -32,33 +37,36 @@ class LcobucciJWSProvider implements JWSProviderInterface
* @param RawKeyLoader $keyLoader
* @param string $cryptoEngine
* @param string $signatureAlgorithm
* @param int $ttl
* @param int|null $ttl
*
* @throws \InvalidArgumentException If the given algorithm|engine is not supported
* @throws \InvalidArgumentException If the given crypto engine is not supported
*/
public function __construct(RawKeyLoader $keyLoader, $cryptoEngine, $signatureAlgorithm, $ttl)
{
if (!is_numeric($ttl)) {
if ('openssl' !== $cryptoEngine) {
throw new \InvalidArgumentException(sprintf('The %s provider supports only "openssl" as crypto engine.', __CLASS__));
}

if (null !== $ttl && !is_numeric($ttl)) {
throw new \InvalidArgumentException(sprintf('The TTL should be a numeric value, got %s instead.', $ttl));
}

$this->keyLoader = $keyLoader;
$this->signer = $this->getSignerForAlgorithm($signatureAlgorithm);
$this->ttl = $ttl;

if ('openssl' !== $cryptoEngine) {
throw new \InvalidArgumentException(sprintf('The %s provider supports only "openssl" as crypto engine.', __CLASS__));
}
}

/**
* {@inheritdoc}
*/
public function create(array $payload)
{
$jws = (new Builder())
->setIssuedAt(time())
->setExpiration(time() + $this->ttl);
$jws = new Builder();
$jws->setIssuedAt(time());

if (null !== $this->ttl) {
$jws->setExpiration(time() + $this->ttl);
}

foreach ($payload as $name => $value) {
$jws->set($name, $value);
Expand Down Expand Up @@ -91,7 +99,8 @@ public function load($token)

return new LoadedJWS(
$payload,
$jws->verify($this->signer, $this->keyLoader->loadKey('public')) && $jws->validate(new ValidationData())
$jws->verify($this->signer, $this->keyLoader->loadKey('public')) && $jws->validate(new ValidationData()),
null !== $this->ttl
);
}

Expand Down
14 changes: 12 additions & 2 deletions Signature/LoadedJWS.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@ final class LoadedJWS
*/
private $state;

/**
* @var bool
*/
private $hasLifetime;

/**
* @param array $payload
* @param bool $isVerified
*/
public function __construct(array $payload, $isVerified)
public function __construct(array $payload, $isVerified, $hasLifetime = true)
{
$this->payload = $payload;
$this->payload = $payload;
$this->hasLifetime = $hasLifetime;

if (true === $isVerified) {
$this->state = self::VERIFIED;
Expand Down Expand Up @@ -79,6 +85,10 @@ public function isInvalid()
*/
private function checkExpiration()
{
if (!$this->hasLifetime) {
return;
}

if (!isset($this->payload['exp']) || !is_numeric($this->payload['exp'])) {
return $this->state = self::INVALID;
}
Expand Down
21 changes: 21 additions & 0 deletions Tests/Functional/CompleteTokenAuthenticationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,27 @@ public function testAccessSecuredRouteWithExpiredToken($fail = true)
return json_decode($response->getContent(), true);
}

public function testExpClaimIsNotSetIfNoTTL()
{
static::bootKernel();
$encoder = static::$kernel->getContainer()->get('lexik_jwt_authentication.encoder');

$r = new \ReflectionProperty(get_class($encoder), 'jwsProvider');
$r->setAccessible(true);
$jwsProvider = $r->getValue($encoder);
\Closure::bind(function () {
$this->ttl = null;
}, $jwsProvider, get_class($jwsProvider))->__invoke();

$token = $encoder->encode(['username' => 'lexik']);
$this->assertArrayNotHasKey('exp', $encoder->decode($token));

static::$client = static::createAuthenticatedClient($token);
static::accessSecuredRoute();

$this->assertSuccessful(static::$client->getResponse());
}

protected function assertFailure(Response $response)
{
$this->assertFalse($response->isSuccessful());
Expand Down
34 changes: 34 additions & 0 deletions Tests/Services/JWSProvider/AbstractJWSProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,40 @@ public function testLoad($jwt)
$this->assertTrue(isset($payload['username']));
}

public function testAllowEmptyTtl()
{
$keyLoader = $this->getKeyLoaderMock();
$keyLoader
->expects($this->at(0))
->method('loadKey')
->with('private')
->willReturn(static::$privateKey);
$keyLoader
->expects($this->at(1))
->method('getPassphrase')
->willReturn('foobar');

$keyLoader
->expects($this->at(2))
->method('loadKey')
->with('public')
->willReturn(static::$publicKey);

$provider = new static::$providerClass($keyLoader, 'openssl', 'RS256', null);
$jws = $provider->create(['username' => 'chalasr']);

$this->assertInstanceOf(CreatedJWS::class, $jws);
$this->assertTrue($jws->isSigned());

$jws = $provider->load($jws->getToken());

$this->assertInstanceOf(LoadedJWS::class, $jws);
$this->assertFalse($jws->isInvalid());
$this->assertFalse($jws->isExpired());
$this->assertTrue($jws->isVerified());
$this->assertArrayNotHasKey('exp', $jws->getPayload());
}

/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The algorithm "wrongAlgorithm" is not supported
Expand Down