From 9f39df671817b84ad56a3d14950421bcbd1942cb Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Wed, 28 Apr 2021 23:53:50 +0200 Subject: [PATCH 1/7] chore(deps): update lcobucci/jwt to ~3.4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a15b39bf3..ce41f5f17 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "illuminate/contracts": "^5.2|^6|^7|^8", "illuminate/http": "^5.2|^6|^7|^8", "illuminate/support": "^5.2|^6|^7|^8", - "lcobucci/jwt": "<3.4", + "lcobucci/jwt": "~3.4", "namshi/jose": "^7.0", "nesbot/carbon": "^1.0|^2.0" }, From 0b87187b4409c7e8b2eb557ed0c7ea376d70aba6 Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Thu, 29 Apr 2021 00:06:15 +0200 Subject: [PATCH 2/7] refactor!: eliminate usages of deprecated lcobucci/jwt methods BREAKING CHANGE: The signature of `Tymon\JWTAuth\Providers\JWT\Lcobucci::__construct()` has been changed, because the previously injected dependencies are deprecated now. But it looks like it has only really been set up the way it was to facilitate testing, and the change should not affect users using it through the ServiceProvider. --- src/Providers/AbstractServiceProvider.php | 2 - src/Providers/JWT/Lcobucci.php | 116 ++++++++++++------ tests/Providers/JWT/LcobucciTest.php | 139 ++++++++++++++++------ 3 files changed, 181 insertions(+), 76 deletions(-) diff --git a/src/Providers/AbstractServiceProvider.php b/src/Providers/AbstractServiceProvider.php index ca312a40e..3bbd3fd41 100644 --- a/src/Providers/AbstractServiceProvider.php +++ b/src/Providers/AbstractServiceProvider.php @@ -167,8 +167,6 @@ protected function registerLcobucciProvider() { $this->app->singleton('tymon.jwt.provider.jwt.lcobucci', function ($app) { return new Lcobucci( - new JWTBuilder(), - new JWTParser(), $this->config('secret'), $this->config('algo'), $this->config('keys') diff --git a/src/Providers/JWT/Lcobucci.php b/src/Providers/JWT/Lcobucci.php index 126bdda30..b4db09e3a 100644 --- a/src/Providers/JWT/Lcobucci.php +++ b/src/Providers/JWT/Lcobucci.php @@ -13,8 +13,7 @@ use Exception; use Illuminate\Support\Collection; -use Lcobucci\JWT\Builder; -use Lcobucci\JWT\Parser; +use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Ecdsa; use Lcobucci\JWT\Signer\Ecdsa\Sha256 as ES256; use Lcobucci\JWT\Signer\Ecdsa\Sha384 as ES384; @@ -22,11 +21,12 @@ use Lcobucci\JWT\Signer\Hmac\Sha256 as HS256; use Lcobucci\JWT\Signer\Hmac\Sha384 as HS384; use Lcobucci\JWT\Signer\Hmac\Sha512 as HS512; -use Lcobucci\JWT\Signer\Keychain; +use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa; use Lcobucci\JWT\Signer\Rsa\Sha256 as RS256; use Lcobucci\JWT\Signer\Rsa\Sha384 as RS384; use Lcobucci\JWT\Signer\Rsa\Sha512 as RS512; +use Lcobucci\JWT\Token\RegisteredClaims; use ReflectionClass; use Tymon\JWTAuth\Contracts\Providers\JWT; use Tymon\JWTAuth\Exceptions\JWTException; @@ -35,42 +35,54 @@ class Lcobucci extends Provider implements JWT { /** - * The Builder instance. + * The Configuration instance. * - * @var \Lcobucci\JWT\Builder + * @var Configuration */ - protected $builder; - - /** - * The Parser instance. - * - * @var \Lcobucci\JWT\Parser - */ - protected $parser; + protected $config; + + /** @var \Lcobucci\JWT\Signer The signer chosen based on the aglo. */ + protected $signer; + /** * Create the Lcobucci provider. * - * @param \Lcobucci\JWT\Builder $builder - * @param \Lcobucci\JWT\Parser $parser - * @param string $secret - * @param string $algo - * @param array $keys + * @param string $secret + * @param string $algo + * @param array $keys + * @param Configuration $config Optional, to pass an existing configuration to be used. * * @return void */ public function __construct( - Builder $builder, - Parser $parser, $secret, $algo, - array $keys + array $keys, + $config = null ) { parent::__construct($secret, $algo, $keys); - - $this->builder = $builder; - $this->parser = $parser; + $this->signer = $this->getSigner(); + + if ( ! is_null($config) ) { + $this->config = $config; + } + elseif ( $this->isAsymmetric() ) { + $this->config = Configuration::forAsymmetricSigner($this->signer, $this->getSigningKey(), $this->getVerificationKey()); + } + else { + $this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText( $this->getSecret() ) ); + } + } + + /** + * Gets the {@see $config} attribute. + * + * @return Configuration + */ + public function getConfig() { + return $this->config; } /** @@ -101,19 +113,49 @@ public function __construct( */ public function encode(array $payload) { - // Remove the signature on the builder instance first. - $this->builder->unsign(); - try { foreach ($payload as $key => $value) { - $this->builder->set($key, $value); + $this->addClaim( $key, $value ); } - $this->builder->sign($this->signer, $this->getSigningKey()); + return $this->config->builder()->getToken($this->config->signer(), $this->config->signingKey())->toString(); } catch (Exception $e) { throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e); } - - return (string) $this->builder->getToken(); + } + + /** + * Adds a claim to the {@see $config}. + * + * @param string $key + * @param mixed $value + */ + protected function addClaim($key, $value) + { + switch ($key) { + case RegisteredClaims::ID: + $this->config->builder()->identifiedBy($value); + break; + case RegisteredClaims::EXPIRATION_TIME: + $this->config->builder()->expiresAt(\DateTimeImmutable::createFromFormat('U', $value)); + break; + case RegisteredClaims::NOT_BEFORE: + $this->config->builder()->canOnlyBeUsedAfter(\DateTimeImmutable::createFromFormat('U', $value)); + break; + case RegisteredClaims::ISSUED_AT: + $this->config->builder()->issuedAt(\DateTimeImmutable::createFromFormat('U', $value)); + break; + case RegisteredClaims::ISSUER: + $this->config->builder()->issuedBy($value); + break; + case RegisteredClaims::AUDIENCE: + $this->config->builder()->permittedFor($value); + break; + case RegisteredClaims::SUBJECT: + $this->config->builder()->relatedTo($value); + break; + default: + $this->config->builder()->withClaim($key, $value); + } } /** @@ -128,16 +170,16 @@ public function encode(array $payload) public function decode($token) { try { - $jwt = $this->parser->parse($token); + $jwt = $this->config->parser()->parse($token); } catch (Exception $e) { throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e); } - - if (! $jwt->verify($this->signer, $this->getVerificationKey())) { + + if (! $this->config->validator()->validate($jwt, ...$this->config->validationConstraints()) ) { throw new TokenInvalidException('Token Signature could not be verified.'); } - return (new Collection($jwt->getClaims()))->map(function ($claim) { + return (new Collection($jwt->claims()->all()))->map(function ($claim) { return is_object($claim) ? $claim->getValue() : $claim; })->toArray(); } @@ -174,7 +216,7 @@ protected function isAsymmetric() protected function getSigningKey() { return $this->isAsymmetric() ? - (new Keychain())->getPrivateKey($this->getPrivateKey(), $this->getPassphrase()) : + InMemory::plainText($this->getPrivateKey(), $this->getPassphrase() ?? '') : $this->getSecret(); } @@ -184,7 +226,7 @@ protected function getSigningKey() protected function getVerificationKey() { return $this->isAsymmetric() ? - (new Keychain())->getPublicKey($this->getPublicKey()) : + InMemory::plainText($this->getPublicKey()) : $this->getSecret(); } } diff --git a/tests/Providers/JWT/LcobucciTest.php b/tests/Providers/JWT/LcobucciTest.php index e3983023c..45096b4b2 100644 --- a/tests/Providers/JWT/LcobucciTest.php +++ b/tests/Providers/JWT/LcobucciTest.php @@ -14,8 +14,15 @@ use Exception; use InvalidArgumentException; use Lcobucci\JWT\Builder; +use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Parser; +use Lcobucci\JWT\Signer; use Lcobucci\JWT\Signer\Key; +use Lcobucci\JWT\Signer\Rsa\Sha256 as RS256; +use Lcobucci\JWT\Token; +use Lcobucci\JWT\Token\DataSet; +use Lcobucci\JWT\Validation\Constraint; +use Lcobucci\JWT\Validation\Validator; use Mockery; use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\TokenInvalidException; @@ -24,6 +31,13 @@ class LcobucciTest extends AbstractTestCase { + /** + * Mocks {@see Configuration}. + * + * @var \Mockery\MockInterface + */ + protected $config; + /** * @var \Mockery\MockInterface */ @@ -33,33 +47,41 @@ class LcobucciTest extends AbstractTestCase * @var \Mockery\MockInterface */ protected $builder; - + /** - * @var \Tymon\JWTAuth\Providers\JWT\Namshi + * @var \Mockery\MockInterface */ - protected $provider; + protected $validator; public function setUp(): void { parent::setUp(); - - $this->builder = Mockery::mock(Builder::class); - $this->parser = Mockery::mock(Parser::class); + + $this->builder = Mockery::mock( Builder::class ); + $this->parser = Mockery::mock( Parser::class ); } /** @test */ public function it_should_return_the_token_when_passing_a_valid_payload_to_encode() { $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - - $this->builder->shouldReceive('unsign')->once()->andReturnSelf(); - $this->builder->shouldReceive('set')->times(count($payload)); - $this->builder->shouldReceive('sign')->once()->with(Mockery::any(), 'secret'); - $this->builder->shouldReceive('getToken')->once()->andReturn('foo.bar.baz'); - + + $dataSet = new DataSet($payload, 'payload'); + + $this->builder->shouldReceive('relatedTo')->once()->andReturnSelf(); // sub + $this->builder->shouldReceive('expiresAt')->once()->andReturnSelf(); // exp + $this->builder->shouldReceive('issuedAt')->once()->andReturnSelf(); // iat + $this->builder->shouldReceive('issuedBy')->once()->andReturnSelf(); // iss + $this->builder + ->shouldReceive('getToken') + ->once() + ->with(\Mockery::type(Signer::class), \Mockery::type(Key::class)) + ->andReturn(new Token\Plain(new DataSet([], 'header'), $dataSet, (new Token\Signature('', 'signature')))); + + /** @var Token $token */ $token = $this->getProvider('secret', 'HS256')->encode($payload); - $this->assertSame('foo.bar.baz', $token); + $this->assertSame('header.payload.signature', $token ); } /** @test */ @@ -69,10 +91,16 @@ public function it_should_throw_an_invalid_exception_when_the_payload_could_not_ $this->expectExceptionMessage('Could not create token:'); $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - - $this->builder->shouldReceive('unsign')->once()->andReturnSelf(); - $this->builder->shouldReceive('set')->times(count($payload)); - $this->builder->shouldReceive('sign')->once()->with(Mockery::any(), 'secret')->andThrow(new Exception); + + $this->builder->shouldReceive('relatedTo')->once()->andReturnSelf(); // sub + $this->builder->shouldReceive('expiresAt')->once()->andReturnSelf(); // exp + $this->builder->shouldReceive('issuedAt')->once()->andReturnSelf(); // iat + $this->builder->shouldReceive('issuedBy')->once()->andReturnSelf(); // iss + $this->builder + ->shouldReceive('getToken') + ->once() + ->with(\Mockery::type(Signer::class), \Mockery::type(Key::class)) + ->andThrow(new Exception); $this->getProvider('secret', 'HS256')->encode($payload); } @@ -81,25 +109,37 @@ public function it_should_throw_an_invalid_exception_when_the_payload_could_not_ public function it_should_return_the_payload_when_passing_a_valid_token_to_decode() { $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - - $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn(Mockery::self()); - $this->parser->shouldReceive('verify')->once()->with(Mockery::any(), 'secret')->andReturn(true); - $this->parser->shouldReceive('getClaims')->once()->andReturn($payload); - - $this->assertSame($payload, $this->getProvider('secret', 'HS256')->decode('foo.bar.baz')); + + $token = Mockery::mock(Token::class); + $dataSet = Mockery::mock(new DataSet($payload, 'payload')); + + $provider = $this->getProvider('secret', 'HS256'); + + $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn($token); + $this->validator->shouldReceive('validate')->once()->with($token, Mockery::any())->andReturnTrue(); + $token->shouldReceive('claims')->once()->andReturn($dataSet); + $dataSet->shouldReceive( 'all')->once()->andReturn($payload); + + $this->assertSame($payload, $provider->decode('foo.bar.baz')); } /** @test */ public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded_due_to_a_bad_signature() { + $token = Mockery::mock(Token::class); + $dataSet = Mockery::mock(new DataSet(['pay','load'], 'payload')); + + $provider = $this->getProvider('secret', 'HS256'); + $this->expectException(TokenInvalidException::class); $this->expectExceptionMessage('Token Signature could not be verified.'); - - $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn(Mockery::self()); - $this->parser->shouldReceive('verify')->once()->with(Mockery::any(), 'secret')->andReturn(false); - $this->parser->shouldReceive('getClaims')->never(); - - $this->getProvider('secret', 'HS256')->decode('foo.bar.baz'); + + $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn($token); + $this->validator->shouldReceive('validate')->once()->with($token, Mockery::any())->andReturnFalse(); + $token->shouldReceive('claims')->never(); + $dataSet->shouldReceive( 'all')->never(); + + $provider->decode('foo.bar.baz'); } /** @test */ @@ -118,22 +158,32 @@ public function it_should_throw_a_token_invalid_exception_when_the_token_could_n /** @test */ public function it_should_generate_a_token_when_using_an_rsa_algorithm() { + $dummyPrivateKey = $this->getDummyPrivateKey(); + $dummyPublicKey = $this->getDummyPublicKey(); + $provider = $this->getProvider( 'does_not_matter', 'RS256', - ['private' => $this->getDummyPrivateKey(), 'public' => $this->getDummyPublicKey()] + ['private' => $dummyPrivateKey, 'public' => $dummyPublicKey] ); $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - - $this->builder->shouldReceive('unsign')->once()->andReturnSelf(); - $this->builder->shouldReceive('set')->times(count($payload)); - $this->builder->shouldReceive('sign')->once()->with(Mockery::any(), Mockery::type(Key::class)); - $this->builder->shouldReceive('getToken')->once()->andReturn('foo.bar.baz'); + + $dataSet = new DataSet($payload, 'payload'); + + $this->builder->shouldReceive('relatedTo')->once()->andReturnSelf(); // sub + $this->builder->shouldReceive('expiresAt')->once()->andReturnSelf(); // exp + $this->builder->shouldReceive('issuedAt')->once()->andReturnSelf(); // iat + $this->builder->shouldReceive('issuedBy')->once()->andReturnSelf(); // iss + $this->builder + ->shouldReceive('getToken') + ->once() + ->with(Mockery::type(RS256::class), Mockery::type(Key::class)) + ->andReturn(new Token\Plain(new DataSet([], 'header'), $dataSet, (new Token\Signature('', 'signature')))); $token = $provider->encode($payload); - $this->assertSame('foo.bar.baz', $token); + $this->assertSame('header.payload.signature', $token); } /** @test */ @@ -174,7 +224,22 @@ public function it_should_return_the_keys() public function getProvider($secret, $algo, array $keys = []) { - return new Lcobucci($this->builder, $this->parser, $secret, $algo, $keys); + $provider = new Lcobucci($secret, $algo, $keys); + + $this->validator = Mockery::mock( \Lcobucci\JWT\Validator::class ); + $this->config = Mockery::mock($provider->getConfig()); + + $provider = new Lcobucci($secret, $algo, $keys, $this->config); + + $this->config->shouldReceive('builder')->andReturn($this->builder); + $this->config->shouldReceive('parser')->andReturn($this->parser); + $this->config->shouldReceive('validator')->andReturn($this->validator); + + $constraint = Mockery::mock(Constraint::class); + $constraint->shouldReceive('assert')->andReturn(); + $this->config->shouldReceive('validationConstraints')->andReturn([$constraint]); + + return $provider; } public function getDummyPrivateKey() From e56260096a9e65d27f37488d36403a6085e6fccb Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Thu, 29 Apr 2021 01:01:11 +0200 Subject: [PATCH 3/7] chore(deps): update lcobucci/jwt to include 4.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ce41f5f17..74efb7560 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "illuminate/contracts": "^5.2|^6|^7|^8", "illuminate/http": "^5.2|^6|^7|^8", "illuminate/support": "^5.2|^6|^7|^8", - "lcobucci/jwt": "~3.4", + "lcobucci/jwt": "^3.4|^4.0", "namshi/jose": "^7.0", "nesbot/carbon": "^1.0|^2.0" }, From 6d70beb9e0de39df97484a019d8a34fa06043e9b Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Thu, 29 Apr 2021 01:25:55 +0200 Subject: [PATCH 4/7] fix: issue with missing validation constraints --- src/Providers/JWT/Lcobucci.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Providers/JWT/Lcobucci.php b/src/Providers/JWT/Lcobucci.php index b4db09e3a..a2361612c 100644 --- a/src/Providers/JWT/Lcobucci.php +++ b/src/Providers/JWT/Lcobucci.php @@ -27,6 +27,7 @@ use Lcobucci\JWT\Signer\Rsa\Sha384 as RS384; use Lcobucci\JWT\Signer\Rsa\Sha512 as RS512; use Lcobucci\JWT\Token\RegisteredClaims; +use Lcobucci\JWT\Validation\Constraint\SignedWith; use ReflectionClass; use Tymon\JWTAuth\Contracts\Providers\JWT; use Tymon\JWTAuth\Exceptions\JWTException; @@ -74,6 +75,11 @@ public function __construct( else { $this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText( $this->getSecret() ) ); } + if ( !count($this->config->validationConstraints()) ) { + $this->config->setValidationConstraints( + new SignedWith($this->signer, $this->getVerificationKey()), + ); + } } /** @@ -217,7 +223,7 @@ protected function getSigningKey() { return $this->isAsymmetric() ? InMemory::plainText($this->getPrivateKey(), $this->getPassphrase() ?? '') : - $this->getSecret(); + InMemory::plainText($this->getSecret()); } /** @@ -227,6 +233,6 @@ protected function getVerificationKey() { return $this->isAsymmetric() ? InMemory::plainText($this->getPublicKey()) : - $this->getSecret(); + InMemory::plainText($this->getSecret()); } } From f882e6ab5879906f1b6d16e0a967294c6a13f856 Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Thu, 29 Apr 2021 01:55:02 +0200 Subject: [PATCH 5/7] fix: claims were not added to the generated token All tokens were generated the same from the library, because the builder instance was not persistent between adding the claims. --- src/Providers/JWT/Lcobucci.php | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Providers/JWT/Lcobucci.php b/src/Providers/JWT/Lcobucci.php index a2361612c..9ab9ba2a1 100644 --- a/src/Providers/JWT/Lcobucci.php +++ b/src/Providers/JWT/Lcobucci.php @@ -13,6 +13,7 @@ use Exception; use Illuminate\Support\Collection; +use Lcobucci\JWT\Builder; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Ecdsa; use Lcobucci\JWT\Signer\Ecdsa\Sha256 as ES256; @@ -45,6 +46,9 @@ class Lcobucci extends Provider implements JWT /** @var \Lcobucci\JWT\Signer The signer chosen based on the aglo. */ protected $signer; + /** @var Builder */ + protected $builder; + /** * Create the Lcobucci provider. @@ -119,11 +123,12 @@ public function getConfig() { */ public function encode(array $payload) { + $this->builder = null; try { foreach ($payload as $key => $value) { $this->addClaim( $key, $value ); } - return $this->config->builder()->getToken($this->config->signer(), $this->config->signingKey())->toString(); + return $this->builder->getToken($this->config->signer(), $this->config->signingKey())->toString(); } catch (Exception $e) { throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e); } @@ -137,30 +142,33 @@ public function encode(array $payload) */ protected function addClaim($key, $value) { + if (!isset($this->builder)) { + $this->builder = $this->config->builder(); + } switch ($key) { case RegisteredClaims::ID: - $this->config->builder()->identifiedBy($value); + $this->builder->identifiedBy($value); break; case RegisteredClaims::EXPIRATION_TIME: - $this->config->builder()->expiresAt(\DateTimeImmutable::createFromFormat('U', $value)); + $this->builder->expiresAt(\DateTimeImmutable::createFromFormat('U', $value)); break; case RegisteredClaims::NOT_BEFORE: - $this->config->builder()->canOnlyBeUsedAfter(\DateTimeImmutable::createFromFormat('U', $value)); + $this->builder->canOnlyBeUsedAfter(\DateTimeImmutable::createFromFormat('U', $value)); break; case RegisteredClaims::ISSUED_AT: - $this->config->builder()->issuedAt(\DateTimeImmutable::createFromFormat('U', $value)); + $this->builder->issuedAt(\DateTimeImmutable::createFromFormat('U', $value)); break; case RegisteredClaims::ISSUER: - $this->config->builder()->issuedBy($value); + $this->builder->issuedBy($value); break; case RegisteredClaims::AUDIENCE: - $this->config->builder()->permittedFor($value); + $this->builder->permittedFor($value); break; case RegisteredClaims::SUBJECT: - $this->config->builder()->relatedTo($value); + $this->builder->relatedTo($value); break; default: - $this->config->builder()->withClaim($key, $value); + $this->builder->withClaim($key, $value); } } From 34d5ef943f197c79b0963b25128e7730da181555 Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Thu, 29 Apr 2021 02:05:20 +0200 Subject: [PATCH 6/7] fix: get value of DateTimeImmutable claims Fixes "Error : Call to undefined method DateTimeImmutable::getValue()" --- src/Providers/JWT/Lcobucci.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Providers/JWT/Lcobucci.php b/src/Providers/JWT/Lcobucci.php index 9ab9ba2a1..f3a41fc1c 100644 --- a/src/Providers/JWT/Lcobucci.php +++ b/src/Providers/JWT/Lcobucci.php @@ -123,7 +123,7 @@ public function getConfig() { */ public function encode(array $payload) { - $this->builder = null; + $this->builder = null; try { foreach ($payload as $key => $value) { $this->addClaim( $key, $value ); @@ -142,12 +142,12 @@ public function encode(array $payload) */ protected function addClaim($key, $value) { - if (!isset($this->builder)) { - $this->builder = $this->config->builder(); - } + if (!isset($this->builder)) { + $this->builder = $this->config->builder(); + } switch ($key) { case RegisteredClaims::ID: - $this->builder->identifiedBy($value); + $this->builder->identifiedBy($value); break; case RegisteredClaims::EXPIRATION_TIME: $this->builder->expiresAt(\DateTimeImmutable::createFromFormat('U', $value)); @@ -194,7 +194,13 @@ public function decode($token) } return (new Collection($jwt->claims()->all()))->map(function ($claim) { - return is_object($claim) ? $claim->getValue() : $claim; + if ( is_a($claim, \DateTimeImmutable::class)) { + return $claim->getTimestamp(); + } + if ( is_object($claim) && method_exists( $claim, 'getValue') ) { + return $claim->getValue(); + } + return $claim; })->toArray(); } From 51307cf6b1324b98b88abf0f55ad7b2dd4a5febf Mon Sep 17 00:00:00 2001 From: BenceSzalai Date: Thu, 29 Apr 2021 02:47:03 +0200 Subject: [PATCH 7/7] style: apply styleci suggestions --- src/Providers/AbstractServiceProvider.php | 2 - src/Providers/JWT/Lcobucci.php | 42 ++++++++--------- tests/Providers/JWT/LcobucciTest.php | 57 +++++++++++------------ 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/Providers/AbstractServiceProvider.php b/src/Providers/AbstractServiceProvider.php index 3bbd3fd41..45d629969 100644 --- a/src/Providers/AbstractServiceProvider.php +++ b/src/Providers/AbstractServiceProvider.php @@ -12,8 +12,6 @@ namespace Tymon\JWTAuth\Providers; use Illuminate\Support\ServiceProvider; -use Lcobucci\JWT\Builder as JWTBuilder; -use Lcobucci\JWT\Parser as JWTParser; use Namshi\JOSE\JWS; use Tymon\JWTAuth\Blacklist; use Tymon\JWTAuth\Claims\Factory as ClaimFactory; diff --git a/src/Providers/JWT/Lcobucci.php b/src/Providers/JWT/Lcobucci.php index f3a41fc1c..0f2c0d0f0 100644 --- a/src/Providers/JWT/Lcobucci.php +++ b/src/Providers/JWT/Lcobucci.php @@ -42,13 +42,12 @@ class Lcobucci extends Provider implements JWT * @var Configuration */ protected $config; - + /** @var \Lcobucci\JWT\Signer The signer chosen based on the aglo. */ protected $signer; - + /** @var Builder */ protected $builder; - /** * Create the Lcobucci provider. @@ -67,31 +66,30 @@ public function __construct( $config = null ) { parent::__construct($secret, $algo, $keys); - + $this->signer = $this->getSigner(); - - if ( ! is_null($config) ) { + + if (! is_null($config)) { $this->config = $config; - } - elseif ( $this->isAsymmetric() ) { + } elseif ($this->isAsymmetric()) { $this->config = Configuration::forAsymmetricSigner($this->signer, $this->getSigningKey(), $this->getVerificationKey()); + } else { + $this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText($this->getSecret())); } - else { - $this->config = Configuration::forSymmetricSigner($this->signer, InMemory::plainText( $this->getSecret() ) ); - } - if ( !count($this->config->validationConstraints()) ) { + if (! count($this->config->validationConstraints())) { $this->config->setValidationConstraints( new SignedWith($this->signer, $this->getVerificationKey()), ); } } - + /** * Gets the {@see $config} attribute. * * @return Configuration */ - public function getConfig() { + public function getConfig() + { return $this->config; } @@ -126,14 +124,15 @@ public function encode(array $payload) $this->builder = null; try { foreach ($payload as $key => $value) { - $this->addClaim( $key, $value ); + $this->addClaim($key, $value); } + return $this->builder->getToken($this->config->signer(), $this->config->signingKey())->toString(); } catch (Exception $e) { throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e); } } - + /** * Adds a claim to the {@see $config}. * @@ -142,7 +141,7 @@ public function encode(array $payload) */ protected function addClaim($key, $value) { - if (!isset($this->builder)) { + if (! isset($this->builder)) { $this->builder = $this->config->builder(); } switch ($key) { @@ -188,18 +187,19 @@ public function decode($token) } catch (Exception $e) { throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e); } - - if (! $this->config->validator()->validate($jwt, ...$this->config->validationConstraints()) ) { + + if (! $this->config->validator()->validate($jwt, ...$this->config->validationConstraints())) { throw new TokenInvalidException('Token Signature could not be verified.'); } return (new Collection($jwt->claims()->all()))->map(function ($claim) { - if ( is_a($claim, \DateTimeImmutable::class)) { + if (is_a($claim, \DateTimeImmutable::class)) { return $claim->getTimestamp(); } - if ( is_object($claim) && method_exists( $claim, 'getValue') ) { + if (is_object($claim) && method_exists($claim, 'getValue')) { return $claim->getValue(); } + return $claim; })->toArray(); } diff --git a/tests/Providers/JWT/LcobucciTest.php b/tests/Providers/JWT/LcobucciTest.php index 45096b4b2..a1ced4346 100644 --- a/tests/Providers/JWT/LcobucciTest.php +++ b/tests/Providers/JWT/LcobucciTest.php @@ -22,7 +22,6 @@ use Lcobucci\JWT\Token; use Lcobucci\JWT\Token\DataSet; use Lcobucci\JWT\Validation\Constraint; -use Lcobucci\JWT\Validation\Validator; use Mockery; use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\TokenInvalidException; @@ -37,7 +36,7 @@ class LcobucciTest extends AbstractTestCase * @var \Mockery\MockInterface */ protected $config; - + /** * @var \Mockery\MockInterface */ @@ -47,7 +46,7 @@ class LcobucciTest extends AbstractTestCase * @var \Mockery\MockInterface */ protected $builder; - + /** * @var \Mockery\MockInterface */ @@ -56,16 +55,16 @@ class LcobucciTest extends AbstractTestCase public function setUp(): void { parent::setUp(); - - $this->builder = Mockery::mock( Builder::class ); - $this->parser = Mockery::mock( Parser::class ); + + $this->builder = Mockery::mock(Builder::class); + $this->parser = Mockery::mock(Parser::class); } /** @test */ public function it_should_return_the_token_when_passing_a_valid_payload_to_encode() { $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - + $dataSet = new DataSet($payload, 'payload'); $this->builder->shouldReceive('relatedTo')->once()->andReturnSelf(); // sub @@ -81,7 +80,7 @@ public function it_should_return_the_token_when_passing_a_valid_payload_to_encod /** @var Token $token */ $token = $this->getProvider('secret', 'HS256')->encode($payload); - $this->assertSame('header.payload.signature', $token ); + $this->assertSame('header.payload.signature', $token); } /** @test */ @@ -91,7 +90,7 @@ public function it_should_throw_an_invalid_exception_when_the_payload_could_not_ $this->expectExceptionMessage('Could not create token:'); $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - + $this->builder->shouldReceive('relatedTo')->once()->andReturnSelf(); // sub $this->builder->shouldReceive('expiresAt')->once()->andReturnSelf(); // exp $this->builder->shouldReceive('issuedAt')->once()->andReturnSelf(); // iat @@ -109,16 +108,16 @@ public function it_should_throw_an_invalid_exception_when_the_payload_could_not_ public function it_should_return_the_payload_when_passing_a_valid_token_to_decode() { $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - + $token = Mockery::mock(Token::class); $dataSet = Mockery::mock(new DataSet($payload, 'payload')); - + $provider = $this->getProvider('secret', 'HS256'); - + $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn($token); $this->validator->shouldReceive('validate')->once()->with($token, Mockery::any())->andReturnTrue(); $token->shouldReceive('claims')->once()->andReturn($dataSet); - $dataSet->shouldReceive( 'all')->once()->andReturn($payload); + $dataSet->shouldReceive('all')->once()->andReturn($payload); $this->assertSame($payload, $provider->decode('foo.bar.baz')); } @@ -127,18 +126,18 @@ public function it_should_return_the_payload_when_passing_a_valid_token_to_decod public function it_should_throw_a_token_invalid_exception_when_the_token_could_not_be_decoded_due_to_a_bad_signature() { $token = Mockery::mock(Token::class); - $dataSet = Mockery::mock(new DataSet(['pay','load'], 'payload')); - + $dataSet = Mockery::mock(new DataSet(['pay', 'load'], 'payload')); + $provider = $this->getProvider('secret', 'HS256'); - + $this->expectException(TokenInvalidException::class); $this->expectExceptionMessage('Token Signature could not be verified.'); - + $this->parser->shouldReceive('parse')->once()->with('foo.bar.baz')->andReturn($token); $this->validator->shouldReceive('validate')->once()->with($token, Mockery::any())->andReturnFalse(); $token->shouldReceive('claims')->never(); - $dataSet->shouldReceive( 'all')->never(); - + $dataSet->shouldReceive('all')->never(); + $provider->decode('foo.bar.baz'); } @@ -159,8 +158,8 @@ public function it_should_throw_a_token_invalid_exception_when_the_token_could_n public function it_should_generate_a_token_when_using_an_rsa_algorithm() { $dummyPrivateKey = $this->getDummyPrivateKey(); - $dummyPublicKey = $this->getDummyPublicKey(); - + $dummyPublicKey = $this->getDummyPublicKey(); + $provider = $this->getProvider( 'does_not_matter', 'RS256', @@ -168,9 +167,9 @@ public function it_should_generate_a_token_when_using_an_rsa_algorithm() ); $payload = ['sub' => 1, 'exp' => $this->testNowTimestamp + 3600, 'iat' => $this->testNowTimestamp, 'iss' => '/foo']; - + $dataSet = new DataSet($payload, 'payload'); - + $this->builder->shouldReceive('relatedTo')->once()->andReturnSelf(); // sub $this->builder->shouldReceive('expiresAt')->once()->andReturnSelf(); // exp $this->builder->shouldReceive('issuedAt')->once()->andReturnSelf(); // iat @@ -225,20 +224,20 @@ public function it_should_return_the_keys() public function getProvider($secret, $algo, array $keys = []) { $provider = new Lcobucci($secret, $algo, $keys); - - $this->validator = Mockery::mock( \Lcobucci\JWT\Validator::class ); + + $this->validator = Mockery::mock(\Lcobucci\JWT\Validator::class); $this->config = Mockery::mock($provider->getConfig()); - + $provider = new Lcobucci($secret, $algo, $keys, $this->config); - + $this->config->shouldReceive('builder')->andReturn($this->builder); $this->config->shouldReceive('parser')->andReturn($this->parser); $this->config->shouldReceive('validator')->andReturn($this->validator); - + $constraint = Mockery::mock(Constraint::class); $constraint->shouldReceive('assert')->andReturn(); $this->config->shouldReceive('validationConstraints')->andReturn([$constraint]); - + return $provider; }