diff --git a/README.md b/README.md index 1a76ff1..f60b454 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,9 @@ This extension provides the [JWT](https://github.com/lcobucci/jwt) integration f > This is fork of [sizeg/yii2-jwt](https://github.com/sizeg/yii2-jwt) package -This package uses lcobucci/jwt v3. For v4 install `bizley/jwt:^3.0`. +This package is compatible with lcobucci/jwt v3.3 or v3.4. +- For v3.2 and older install `bizley/jwt:<2.1`. +- For v4 install `bizley/jwt:^3.0`. ## Installation @@ -17,11 +19,11 @@ Add the package to your `composer.json`: { "require": { - "bizley/jwt": "^2.0" + "bizley/jwt": "^2.1" } } -and run `composer update` or alternatively run `composer require bizley/jwt:^2.0` +and run `composer update` or alternatively run `composer require bizley/jwt:^2.1` ## Basic usage @@ -64,7 +66,7 @@ For other configuration options refer to the [Yii 2 Guide](https://www.yiiframew ### JWT Basic Usage -Please refer to the [lcobucci/jwt Documentation](https://github.com/lcobucci/jwt/blob/3.2/README.md). +Please refer to the [lcobucci/jwt Documentation](https://lcobucci-jwt.readthedocs.io/en/3.4.0/). ## JSON Web Tokens diff --git a/composer.json b/composer.json index b1e1c0d..1f5a7c7 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require": { "php": ">=7.1.0", - "lcobucci/jwt": "^3.2", + "lcobucci/jwt": ">3.2 <4.0", "yiisoft/yii2": ">=2.0.14 <2.1" }, "require-dev": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index edbf5f5..d8f5a3c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,9 @@ + convertErrorsToExceptions="true" + convertNoticesToExceptions="false" + convertWarningsToExceptions="true" + stopOnFailure="false"> ./tests diff --git a/src/Jwt.php b/src/Jwt.php index c416591..ebced1e 100644 --- a/src/Jwt.php +++ b/src/Jwt.php @@ -9,8 +9,10 @@ use Lcobucci\JWT\Parser; use Lcobucci\JWT\Parsing\Decoder; use Lcobucci\JWT\Parsing\Encoder; +use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Token; use Lcobucci\JWT\ValidationData; +use LogicException; use Yii; use yii\base\Component; @@ -19,7 +21,7 @@ * @see https://github.com/lcobucci/jwt * * @author Dmitriy Demin original package - * @author Paweł Bizley Brzozowski since 2.0 (fork) + * @author Paweł Bizley Brzozowski since 2.0.0 (fork) */ class Jwt extends Component { @@ -58,23 +60,25 @@ public function getBuilder(?Encoder $encoder = null, ?Factory $claimFactory = nu /** * Initializes parser. + * Since 2.1.0 argument $claimFactory is removed. * @param Decoder|null $decoder - * @param Factory|null $claimFactory * @return Parser */ - public function getParser(?Decoder $decoder = null, ?Factory $claimFactory = null): Parser + public function getParser(?Decoder $decoder = null): Parser { - return new Parser($decoder, $claimFactory); // $claimFactory not used anymore in lcobucci/jwt 3.4 + return new Parser($decoder); } /** * Initializes validation data wrapper. + * Since 2.1.0 argument $leeway is added. * @param int|null $currentTime UNIX timestamp or null for time() + * @param int|null $leeway * @return ValidationData */ - public function getValidationData(?int $currentTime = null): ValidationData + public function getValidationData(?int $currentTime = null, ?int $leeway = 0): ValidationData { - return new ValidationData($currentTime); + return new ValidationData($currentTime, $leeway); } /** @@ -156,29 +160,30 @@ public function verifyToken(Token $token): bool /* @var $signer \Lcobucci\JWT\Signer */ $signer = Yii::createObject($this->signers[$alg]); - return $token->verify($signer, $this->prepareKey($this->key)); + return $token->verify($signer, $this->prepareKeyObject($this->key)); } /** - * Detects key file path and resolves Yii alias if given. + * Detects key file path and resolves Yii alias if given and returns key string. * @param string $key * @return string|null - * @throws \LogicException when file path does not exist or is not readable - * @since 2.0 + * @throws LogicException when file path does not exist or is not readable + * @since 2.0.0 + * @deprecated since 2.1.0, use prepareKeyObject() instead */ public function prepareKey(string $key): ?string { $keyPath = null; if (strpos($key, '@') === 0) { - $keyPath = 'file://' . Yii::getAlias($key); + $keyPath = Yii::getAlias($key); } elseif (strpos($key, 'file://') === 0) { $keyPath = $key; } if ($keyPath !== null) { if (!file_exists($keyPath) || !is_readable($keyPath)) { - throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); + throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); } return $keyPath; @@ -186,4 +191,32 @@ public function prepareKey(string $key): ?string return $key; } + + /** + * Detects key file path and resolves Yii alias if given and returns key object. + * @param string $key + * @return InMemory|null + * @throws LogicException when file path does not exist or is not readable + * @since 2.1.0 + */ + public function prepareKeyObject(string $key): ?InMemory + { + $keyPath = null; + + if (strpos($key, '@') === 0) { + $keyPath = Yii::getAlias($key); + } elseif (strpos($key, 'file://') === 0) { + $keyPath = $key; + } + + if ($keyPath !== null) { + if (!file_exists($keyPath) || !is_readable($keyPath)) { + throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath)); + } + + return InMemory::file($keyPath); + } + + return $key ? InMemory::plainText($key) : null; + } } diff --git a/tests/BearerTest.php b/tests/BearerTest.php index 0963d12..790e81b 100644 --- a/tests/BearerTest.php +++ b/tests/BearerTest.php @@ -6,6 +6,8 @@ use bizley\jwt\Jwt; use bizley\jwt\JwtHttpBearerAuth; +use DateTimeImmutable; +use Lcobucci\JWT\Signer\Hmac\Sha256; use PHPUnit\Framework\TestCase; use Yii; use yii\base\InvalidConfigException; @@ -69,9 +71,9 @@ public function testHttpBearerAuthInvalidToken(): void public function testHttpBearerAuthExpiredToken(): void { $token = Yii::$app->jwt->getBuilder() - ->setIssuedAt(time() - 100) - ->setExpiration(time() - 50) - ->getToken(); + ->setIssuedAt((new DateTimeImmutable())->setTimestamp(time() - 100)) + ->setExpiration((new DateTimeImmutable())->setTimestamp(time() - 50)) + ->getToken(new Sha256(), Yii::$app->jwt->prepareKeyObject(Yii::$app->jwt->key)); Yii::$app->request->headers->set('Authorization', "Bearer $token"); @@ -92,11 +94,10 @@ public function testHttpBearerAuthExpiredToken(): void public function testHttpBearerAuth(): void { $token = Yii::$app->jwt->getBuilder() - ->setIssuedAt(time()) - ->setExpiration(time() + 3600) + ->setIssuedAt((new DateTimeImmutable())->setTimestamp(time())) + ->setExpiration((new DateTimeImmutable())->setTimestamp(time() + 3600)) ->setSubject('test') - ->sign(new \Lcobucci\JWT\Signer\Hmac\Sha256(), Yii::$app->jwt->key) - ->getToken(); + ->getToken(new Sha256(), Yii::$app->jwt->prepareKeyObject(Yii::$app->jwt->key)); UserIdentity::$token = (string) $token; @@ -114,11 +115,10 @@ public function testHttpBearerAuth(): void public function testHttpBearerAuthCustom(): void { $token = Yii::$app->jwt->getBuilder() - ->setIssuedAt(time()) - ->setExpiration(time() + 3600) + ->setIssuedAt((new DateTimeImmutable())->setTimestamp(time())) + ->setExpiration((new DateTimeImmutable())->setTimestamp(time() + 3600)) ->setSubject('test') - ->sign(new \Lcobucci\JWT\Signer\Hmac\Sha256(), Yii::$app->jwt->key) - ->getToken(); + ->getToken(new Sha256(), Yii::$app->jwt->prepareKeyObject(Yii::$app->jwt->key)); Yii::$app->request->headers->set('Authorization', "Bearer $token"); diff --git a/tests/HmacVerifyTest.php b/tests/HmacVerifyTest.php index fbb1024..42575a7 100644 --- a/tests/HmacVerifyTest.php +++ b/tests/HmacVerifyTest.php @@ -4,10 +4,7 @@ namespace bizley\tests; -use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer; -use Lcobucci\JWT\Token; -use yii\base\InvalidConfigException; class HmacVerifyTest extends SignerTestCase { @@ -19,23 +16,8 @@ public function getSigner(): Signer return new \Lcobucci\JWT\Signer\Hmac\Sha256(); } - /** - * @param Builder $builder - * @return Builder - * @throws InvalidConfigException - */ - public function sign(Builder $builder): Builder - { - return $builder->sign($this->getSigner(), $this->getJwt()->key); - } - - /** - * @param Token $token - * @return bool - * @throws InvalidConfigException - */ - public function verify(Token $token): bool + public function getSigningKey(): string { - return $token->verify($this->getSigner(), $this->getJwt()->key); + return $this->getJwt()->key; } } diff --git a/tests/RsaVerifyTest.php b/tests/RsaVerifyTest.php index a023f2c..b87882a 100644 --- a/tests/RsaVerifyTest.php +++ b/tests/RsaVerifyTest.php @@ -4,17 +4,12 @@ namespace bizley\tests; -use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer; -use Lcobucci\JWT\Token; -use yii\base\InvalidConfigException; class RsaVerifyTest extends SignerTestCase { public $jwtConfig = ['key' => '@bizley/tests/data/rsa.key.pub']; - private $privateKey = '@bizley/tests/data/rsa.key'; - /** * @return Signer */ @@ -23,23 +18,8 @@ public function getSigner(): Signer return new \Lcobucci\JWT\Signer\Rsa\Sha256(); } - /** - * @param Builder $builder - * @return Builder - * @throws InvalidConfigException - */ - public function sign(Builder $builder): Builder - { - return $builder->sign($this->getSigner(), $this->getJwt()->prepareKey($this->privateKey)); - } - - /** - * @param Token $token - * @return bool - * @throws InvalidConfigException - */ - public function verify(Token $token): bool + public function getSigningKey(): string { - return $token->verify($this->getSigner(), $this->getJwt()->prepareKey($this->getJwt()->key)); + return '@bizley/tests/data/rsa.key'; } } diff --git a/tests/SignerTestCase.php b/tests/SignerTestCase.php index 074fee7..4889703 100644 --- a/tests/SignerTestCase.php +++ b/tests/SignerTestCase.php @@ -5,7 +5,6 @@ namespace bizley\tests; use bizley\jwt\Jwt; -use Lcobucci\JWT\Builder; use Lcobucci\JWT\Signer; use Lcobucci\JWT\Token; use PHPUnit\Framework\TestCase; @@ -38,9 +37,7 @@ public function getJwt(): Jwt abstract public function getSigner(): Signer; - abstract public function sign(Builder $builder): Builder; - - abstract public function verify(Token $token): bool; + abstract public function getSigningKey(): string; /** * @return Token @@ -48,14 +45,18 @@ abstract public function verify(Token $token): bool; */ public function createTokenWithSignature(): Token { - return $this->sign($this->getJwt()->getBuilder())->getToken(); + return $this->getJwt()->getBuilder()->getToken( + $this->getSigner(), + $this->getJwt()->prepareKeyObject($this->getSigningKey()) + ); } /** * @throws InvalidConfigException + * @throws \yii\base\NotSupportedException */ public function testValidateTokenWithSignature(): void { - self::assertTrue($this->verify($this->createTokenWithSignature())); + self::assertTrue($this->getJwt()->verifyToken($this->createTokenWithSignature())); } } diff --git a/tests/TokenValidationTest.php b/tests/TokenValidationTest.php index e403cc9..9a68b85 100644 --- a/tests/TokenValidationTest.php +++ b/tests/TokenValidationTest.php @@ -5,6 +5,8 @@ namespace bizley\tests; use bizley\jwt\Jwt; +use DateTimeImmutable; +use Lcobucci\JWT\Signer\Hmac\Sha256; use Lcobucci\JWT\Token; use PHPUnit\Framework\TestCase; use yii\base\InvalidConfigException; @@ -48,11 +50,11 @@ public function createToken(int $issuedOffset = 0): Token return $this->getJwt()->getBuilder() ->setIssuer(static::$issuer) ->setAudience(static::$audience) - ->setId(static::$id, true) - ->setIssuedAt(time() + $issuedOffset) - ->setExpiration(time() + 3600) + ->setId(static::$id) + ->setIssuedAt((new DateTimeImmutable())->setTimestamp(time() + $issuedOffset)) + ->setExpiration((new DateTimeImmutable())->setTimestamp(time() + 3600)) ->set('uid', 1) - ->getToken(); + ->getToken(new Sha256(), $this->getJwt()->prepareKeyObject($this->getJwt()->key)); } /**